OpenGL Style In OO Land

Moderator
Posts: 1,560
Joined: 2003.10
Post: #16
Only if you're hard-coding them... Blink

- Alex Diener
Quote this message in a reply
Moderator
Posts: 3,573
Joined: 2003.06
Post: #17
@Andrew: For my sprite engine the render method is only about thirty lines long, with fluff. My static mesh renderer takes up fifteen lines for creating the display list, and the display list calling method is only about a half a dozen more. It doesn't take nearly as much gl code to get a lot of stuff done as one might think. Certainly not as much as I once used to believe! Like you mentioned in your posting of this thread, the rendering method gets passed the individual entities' info like where they are, how they're rotated, what's their transparency, and what is their geometry. The rendering method itself is simple and generic. The objects, or other methods like physics routines, do all the heavy lifting as far as calculating their position and geometry and such.
Quote this message in a reply
⌘-R in Chief
Posts: 1,256
Joined: 2002.05
Post: #18
Alright, well someone give an example of how you'd store the geometry in one class and shove everything down through the same rendering function, turning states off and on as needed for that specific thing being rendered.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #19
this is the interface that the renderer classes for one of my current projects implement:

Code:
#ifndef CRenderer_hpp
#define CRenderer_hpp

#include "CMatrix.hpp"

class CShader;

struct SVertex
{
    float s, t;
    float nx, ny, nz;
    float x, y, z;
    
    SVertex(float inX, float inY, float inZ,
            float inNX, float inNY, float inNZ,
            float inS, float inT)
        : s(inS), t(inT),
          nx(inNX), ny(inNY), nz(inNZ),
          x(inX), y(inY), z(inZ)
    {}
    
    SVertex()
    {}
};

struct SBillboard
{
    float x, y, z;
    float width, height;
    float parameter;
    
    SBillboard(float inX, float inY, float inZ,
               float inWidth, float inHeight,
               float inParameter)
        : x(inX), y(inY), z(inZ),
          width(inWidth), height(inHeight),
          parameter(inParameter)
    {}
};

struct SAxialBillboard
{
    float head_x, head_y, head_z;
    float axis_x, axis_y, axis_z;
    float width, length;
    float parameter;
    
    SAxialBillboard(float inHeadX, float inHeadY, float inHeadZ,
                    float inAxisX, float inAxisY, float inAxisZ,
                    float inWidth, float inLength,
                    float inParameter)
        : head_x(inHeadX), head_y(inHeadY), head_z(inHeadZ),
          axis_x(inAxisX), axis_y(inAxisY), axis_z(inAxisZ),
          width(inWidth), length(inLength),
          parameter(inParameter)
    {}
};

class CRenderer
{
public:
    static CRenderer *Instance();
    
    static void SetRenderer(CRenderer *inRenderer);

public:
    virtual ~CRenderer() {}

    virtual void OpenGLStateInvalidated() = 0;
    
    virtual void SetViewSize(
        unsigned inWidth,
        unsigned inHeight) = 0;

    virtual void SetCamera(
        float          inVerticalFieldOfView,
        float          inAspectRatio,
        float          inHither,
        float          inYon,
        const CVector &inCameraLocation,
        const CVector &inTargetLocation,
        const CVector &inUpVector) = 0;

    virtual void SubmitGeometry(
        const CShader        *inShader,
        size_t                inVertexCount,
        const SVertex        *inVertices,
        size_t                inIndexCount,
        const unsigned short *inIndices,
        const CMatrix        &inMatrix) = 0;
    
    virtual void SubmitGeometryNoDepthWrite(
        const CShader        *inShader,
        size_t                inVertexCount,
        const SVertex        *inVertices,
        size_t                inIndexCount,
        const unsigned short *inIndices,
        const CMatrix        &inMatrix) = 0;
    
    virtual void SubmitBillboards(
        const CShader    *inShader,
        size_t            inBillboardCount,
        const SBillboard *inBillboards,
        const CMatrix    &inMatrix) = 0;
        
    virtual void SubmitAxialBillboards(
        const CShader         *inShader,
        size_t                 inBillboardCount,
        const SAxialBillboard *inBillboards,
        const CMatrix         &inMatrix) = 0;
        
    virtual void SubmitHUDGeometry(
        const CShader        *inShader,
        size_t                inVertexCount,
        const SVertex        *inVertices,
        size_t                inIndexCount,
        const unsigned short *inIndices,
        const CMatrix        &inMatrix) = 0;

    virtual void SubmitHUDGeometryOverWorldPoint(
        const CShader        *inShader,
        size_t                inVertexCount,
        const SVertex        *inVertices,
        size_t                inIndexCount,
        const unsigned short *inIndices,
        const CVector        &inLocation) = 0;

    virtual void BeginFrame() = 0;
    virtual void FinishFrame() = 0;
    
private:
    static CRenderer *sInstance;
};

#define RENDERER (CRenderer::Instance())

#endif

I then have a CNaiveRenderer which just draws everything with glDrawElements in the implementations of the various Submit* methods, and a CRetainedRenderer which stores the geometry it's passed, batches it up, sorts by shader, etc, then does all the drawing with VBOs in the EndFrame method. CNaiveRenderer is faster in debug mode, but has some graphical glitches. CRetainedRenderer is faster in optimized builds, and renders everything properly.

The interface certainly isn't perfect, and will need modification before I can do all the visual effects I want, but it should give you an idea of what you can do.
Quote this message in a reply
Moderator
Posts: 3,573
Joined: 2003.06
Post: #20
Hmm... It's not real easy to give a short example for this one I'm afraid (not that I could think of anyway). I came up with a stupid short kinda/sorta pseudo code example in C if that might help. Anything bigger than this and it would require a full demo. So I left out everything I could, including the entire Sprite class and linked list handling code, amongst all other stuff.

The Sprite structure would represent your Sprite class. InitStates gets called once externally before drawing comences. SetTexturingState is a state caching method to avoid sending unneeded state changes to OpenGL. DrawScene is the core of the rendering code and gets called once per frame.

Code:
typedef struct _Sprite
{
    int      textureName;
    float    uLeft, uRight, vBottom, vTop;
    float    left, right, bottom, top;
    float    x;
    float    y;
    float    rotation;
    float    size;
    float    opacity;
    Boolean  useTexture;
    
    void    (*Update)(struct _Sprite *entity);
    
    struct _Sprite  *prev, *next;
} Sprite;

// big linked list of sprites
Sprite    *firstSprite;

void InitStates(void)
{    
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_FOG);
    glDisable(GL_LIGHTING);
}

void SetTexturingState(Boolean state)
{
    static Boolean    cachedState = false;
    
    if (state == cachedState) return;
    
    cachedState = state;
    if (state == true)
    {
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);
    }
    else
    {
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_BLEND);
    }
}

void DrawScene(void)
{
    Sprite    *sprite;
    
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, 1.0, 0, 1.0, 1.0, -1.0);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    
    // update all sprites
    sprite = firstSprite;
    while (sprite != NULL)
    {
        if (sprite->update)
            sprite->update(sprite);
        sprite = sprite->next;
    }
    
    // draw all sprites
    sprite = firstSprite;
    while (sprite != NULL)
    {
        DrawSprite(sprite);
        sprite = sprite->next;
    }
    
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
}

void DrawSprite(Sprite *sprite)
{
    glLoadIdentity();
    glTranslatef(sprite->x, sprite->y, 0.0f);
    glScalef(sprite->size, sprite->size, 1.0f);
    glRotatef(sprite->rotation, 0.0f, 0.0f, 1.0f);
    glColor4f(1.0f, 1.0f, 1.0f, sprite->opacity);
    if (sprite->useTexture)
    {
        SetTexturingState(true);
        glBindTexture(GL_TEXTURE_2D, sprite->textureName);
    }
    else
        SetTexturingState(false);
    
    glBegin(GL_QUADS);
        glTexCoord2f(sprite->uLeft, sprite->vBottom);
        glVertex2f(sprite->left, sprite->bottom);
        glTexCoord2f(sprite->uRight, sprite->vBottom);
        glVertex2f(sprite->right, sprite->bottom);
        glTexCoord2f(sprite->uRight, sprite->vTop);
        glVertex2f(sprite->right, sprite->top);
        glTexCoord2f(sprite->uLeft, sprite->vTop);
        glVertex2f(sprite->left, sprite->top);
    glEnd();
}
Quote this message in a reply
Member
Posts: 208
Joined: 2005.04
Post: #21
Thanks for the examples guys Smile

BTW, I'm coding in objective-C, it's all 2D stuff, and there aren't any sprites or textures of any kind (it's vector-looking stuff).
Quote this message in a reply
Member
Posts: 129
Joined: 2005.02
Post: #22
Definitely in agreement with OSC. If you change things with rendering, you will have to adjust all those model objects. Centralized rendering sounds key. Esp if you want to support different rendering APIs (not too much to choose from on the Mac OS X side, though)

Mark
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #23
I took a fairly different approach. I've made my objects inherit from an interface, "SetDrawable" which stipulates a setup method, teardown method, display method ( which makes no changes to gl state ) and a method which *describes* the object's required gl state -- which is to say, bounds textures, materials, blending, etc etc etc.

When my scenegraph gathers up the objects to draw, it queries them for their required gl states to display, and then sorts them such that objects which require the same setup are contiguously stored in an array. Then as I iterate that array, I only call the setup and teardown methods on the boundaries between objects with differing gl state requirements.

So long as I've got more than one object with the same drawing requirements -- and in a game this isn't uncommon, where you might have 10 enemies using the same models, same textures, etc -- this is a fair performance boost. In one of my demos I've got 240 -- two hundred and forty! -- models, of which there are about 10 different drawing methods and so long as shadows aren't enabled I get 40 fps.

I get to keep a nice OO design, but I still get to minimize gl state changes.
Quote this message in a reply
Member
Posts: 567
Joined: 2004.07
Post: #24
*gasps*
Is OSC using C++?

It's not magic, it's Ruby.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #25
Not my choice Rasp
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  old style engine and other stuff stuepfnick 3 2,577 Feb 15, 2003 09:08 PM
Last Post: henryj