Mmk, I give up, lets try your way. (Resource Management)

This is a discussion on Mmk, I give up, lets try your way. (Resource Management) within the Game Programming forums, part of the General Programming Boards category; Okay, so I've tried templates, I've tried creating classes upon classes, upon classes, all for one single goal. I just ...

  1. #1
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    964

    Mmk, I give up, lets try your way. (Resource Management)

    Okay, so I've tried templates, I've tried creating classes upon classes, upon classes, all for one single goal. I just want to organize data for all objects in my game and be able to access them in like, 1 or 2 lines.

    The idea is to create a bunch of storage structures that hold data, and create a vector of those storage structures, then create a vector of those vectors, and have a big organized pool of data all plopped together.

    I'm gonna pull a piece of code from a very very long thread on the third or second page of the board, and make a new topic related only to that single reply.

    Ok guys let me add my two cents here and that's probably what it will be worth.

    First
    This design is already so complicated that you cannot even discuss it correctly.
    Therefore - scrap it. It won't add to speed, graphics, gameplay, or even manage objects well. What it will do as evidenced by this thread is confuse the hell out of ya.
    Scrap it.
    Using one function as an overall function for rendering is a great idea, however, you are approaching it incorrectly.

    Now pay attention because this is what Dae and CornedBee and Bob and all of us are saying. The ONLY common portion of any graphical object to be rendered are:

    Vertex data
    Texture data
    Physics data

    Here is my suggestion. Throw templates out the window and throw the class inheritance virtual function thingy out the window. Too complicated. The problem with C++ inheritance is that when you look at your code you cannot tell at a glance what is happening because the inheritance is so deep.

    Get my drift here. I don't like virtual functions because they obfuscate the code quite easily with more than 1 derivation w/o adding that much flexibility to the engine.

    I don't like this:


    Code:
    class Base
    {
    
      virtual void Render()=0;
    }
    class Derived1:public Object
    {
      void Render();
    }
    
    class Derived2:public Object
    {
      void Render();
    }
    
    class Derived3:public Object
    {
      void Render();
    }
    Way too many renders for something that can be done in one function.

    Here is what I do like:

    1. Each object allocates it's vertex memory from a huge pool.
    2. The pool manager will return a starting index and an ending index for the data.
    3. Texture coord data is contained within the pool.
    4. Physics data is contained within the pool.

    Like this - ALL objects will have the following information, but not ALL objects will use every field in the structures. Texture coord data is contained in the VertexData struct I'll show this using vectors for now, later you would change to a static array IF your framerates suffered. I doubt they will.

    Code:
    struct PhysicsData
    {
    };
    
    class PhysicsContainer
    {
    }
    
    struct VertexData
    {
    };
    
    class VertexContainer
    {
    };
    
    
    struct PositionData
    {
    };
    
    class PositionContainer
    {
    };
    
    class MatrixData
    {
    };
    
    class MatrixContainer
    {
    };
    
    class TextureData
    {
    };
    
    class TextureContainer
    {
    };
    
    
    struct ObjectInfo
    {
      DWORD dwStartIndex;
      DWORD dwEndIndex;
      DWORD dwID;
      DWORD *dwTextureIDs;
    };
    
    
    class Object
    {
       ObjectInfo ObjectData;
    
       //One object may have more matrices
       //This is for transformation
       MatrixContainer        Matrices;
    
    };
    
    class ResourceCache
    {
      PhysicsContainer   Physics;
      VertexContainer    Vertices;
      PositionContainer  Positions;
      TextureContainer  Textures;
    };
    
    class RenderList
    {
      ResourceCache  *RenderData;
      std::vector<DWORD>  ObjectIDs;
    };
    So now all the main areas of information are encapsulated for each object and the rendering list simply has a vector that can be added to in order to create the render list. I would try to use a static array but the render list is built every frame so it's a bit tedious and time consuming.


    The final render list simply holds a list of IDs that are indices into the other containers for that object. So.

    Everything is based on IDs. The final result is that everything amounts to:

    1 function call, 1 lookup.

    If you make the arrays or vectors public in the container classes, then you don't even have to do the function call.

    This is a bit complex, but in the end it is very easy to use - it's just hard to setup.

    Sounds would use the same idea. All sounds reside in a sound container and can be accessed via their ID number.

    The final render list is simply a huge list of IDs of the objects that are visible in the current frame. The transformation engine then just iterates through this list, grabs the vertex and position information for that ID, grabs the matrix stack (if applicable) from the object, and transforms the object. Then the renderer will grab the texture data, etc, etc.

    This is a work in progress so there is still stuff to work out, but I hope you get what I'm saying here.

    Right now, I got object classes that look like this

    Code:
    class objectfoo : object
    {
    public:
    get renderdata();
    get physicsdata();
    get sounddata();
    update();
    };
    Okay, so all I want those simple little functions do is return something that gives me access to that specific bit of data, (or maybe one function that grabs em all, who knows)... With your system, what would I be grabbing? An ID to an object I've loaded into your data structures? Thats pretty much it right? Then your uber data management structures handle the rest.

    I just want more details pertaining to this setup, can you grant em to me, oh Bubba the Pirate.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  2. #2
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    964
    I got this so far...

    Code:
    #include "windows.h"
    
    struct MaterialData
    {
    	float Ambient[4], Diffuse[4], Specular[4], Emissive[4];
    	float Shininess;
    	int Texture;
    	char *TextureFilename;
    };
    
    class MaterialContainer
    {
    };
    
    struct PhysicsData
    {
    	double mass;
    	double velocity[3];
    };
    
    class PhysicsContainer
    {
    };
    
    struct VertexData
    {
    	byte m_flags;
    	float m_vertex[3];
    	char m_boneID;
    	byte m_refCount;
    };
    
    class VertexContainer
    {
    };
    
    struct PositionData
    {
    	float pos[3];
    };
    
    class PositionContainer
    {
    };
    
    class MatrixData
    {
    };
    
    class MatrixContainer
    {
    };
    
    
    struct ObjectInfo
    {
    	DWORD dwStartIndex;
    	DWORD dwEndIndex;
    	DWORD dwID;
    	DWORD *dwTextureIDs;
    };
    
    class ResourceCache
    {
    	PhysicsContainer   Physics;
    	VertexContainer    Vertices;
    	PositionContainer  Positions;
    	MaterialContainer  Materials;
    };
    
    class RenderList
    {
    	ResourceCache *RenderData;
    	std::vector<DWORD> ObjectIDs;
    };
    I'm curious, what is um, matrix data? Don't say data pertaining to the matrix. A matrix is an array, what matrix are we talking about here?

    So my next step is to build a Register Object class, assigns an object an ObjectID. Then once we load the information we send its starting value and ending value to the ObjectInfo structure and whatever textures we want to use.

    Then poof, we have an Object of resource data, we lookup the Object in the vector with an Object ID and poof, we instantly have access to all the data pertaining to a single object!

    Sweetness.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  3. #3
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    964
    Alright, learning as I go here, beginning to understand it alot more.


    I had a few questions....

    How do resources get tied with ID's? When you register an object, and you generate an objectID, how do you know what resources to load for that newly registered object? Simple!

    Well, in your object class, say a troll for instance, you simply put a data type that contains a filename to a troll model. In your register function, it is going to take an object type in the parameter info.

    Once you have that done, you access the passed objects data types that contain file paths.. Then you check to see if the files have been loaded, if not, you load them, and throw them into the resource manager. Remember you registered that certain object class and it generated an ID for it, so when you pass that ID again it automatically points to that objects resources in all the different parts of the resource manager, ObjectID 1 accesses vertex data, physics data, and texture data for that object.


    Thats what I got out of it so far.. Lots of grey areas though.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  4. #4
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    I had a few questions....

    How do resources get tied with ID's? When you register an object, and you generate an objectID, how do you know what resources to load for that newly registered object? Simple!
    Did you just ask two questions and reply to yourself that they were simple answers and even answer them?

    Quote Originally Posted by Shamino
    I'm curious, what is um, matrix data? Don't say data pertaining to the matrix. A matrix is an array, what matrix are we talking about here?
    A class to encapsulate a 4x4 array, with operations (rotation, transformation, normalization, etc) and/or coords for each object?

    Okay, so all I want those simple little functions do is return something that gives me access to that specific bit of data, (or maybe one function that grabs em all, who knows)... With your system, what would I be grabbing? An ID to an object I've loaded into your data structures? Thats pretty much it right? Then your uber data management structures handle the rest.
    I don't know if Bubba wants to revisit this topic again, but I'll go over what I got out of it. Mostly similar to what you said.

    The __Container class holds a vector of the __Data class. The pool manager (ResourceCache) then keeps track of all the containers and therefor all the vectors. Then theres an object manager, which you create objects with, and it will retrieve the start/end index from the pool manager and sends that data to be assigned in the newly created object's ObjectInfo. You simply perform operations using the ID of the object (passing it around and such), and the render list (RenderList) has a vector (or static array) of the IDs to be rendered (before frustrum and whatever that removes whats not in view). The data for the objects would be accessed in the render list like so (depending on your setup): ObjectManager->Object(ObjectIDs[i])->ObjectInfo.dwTextureIDs and RenderData->Materials.WhateverYouNeed(ObjectManager->Object(ObjectIDs[i])->ObjectInfo.dwStartIndex, ObjectManager->Object(ObjectIDs[i])->ObjectInfo.dwEndIndex) or whatever your preference of retrieving. Bubba stated that get renderdata(); etc. in object isn't right, but technically its possibly, it would just be unwise because you would have to pass pointers to the containers to every object - which could lead to problems (even if you were using smart pointers). A pointer to the pool manager (ResourceCache) is passed on construction of the render list at every frame.

    I think thats what I got out of it.. its been a few weeks since I've been on the subject (due to school). If you don't care what I think I understand, but personally I think its a good idea to rebound ideas in order to see different perspectives incase you missed something (which is half the reason I wrote this), and honestly its probably a very small jump to visualize your idea to my idea than to Bubba's which is a combination of expert knowledge (after his posts I read about 5 tutorials somewhat related to what he was talking about, and none of them came close to the detail required). Anyway.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  5. #5
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    964
    I've found that problem too Dae, some of them will go as far as plopping some code down in front of you, but none of them explain it properly..

    What you wrote up there did help a bit, any little snippets help.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  6. #6
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,586
    I'm not sure how to explain it any clearer than I already have.

    Think of this. We are Super Mario Bros on the good old NES. There are about 20 little turtle dudes walking to and fro on the screen and about 50 'platforms' we can jump on, all composed of blocks of the same graphic. The background image is a picture of a sky but is composed of many tiles to make up the final picture.

    Now what would be an ideal way to store these objects?

    They all have 1 thing in common. They are 1 bitmap used several times, be it in an animation, tile map, etc.

    If we create an instance of each object, we are wasting precious memory. What you want to do is create 1 instance of the object in memory and use it multiple times. In order to do this you must re-think your C++ programming. New and delete are not your friends here. New allocates malloc(bytesize*sizeof(Data_Type)) bytes from the heap. We don't want that.

    So watch.

    First we create a class to handle the simplest object in our little game screen I just described. The simplest object is the tiles that make up the screen. But again, we don't want a bunch of tile objects in memory. So what is a tile map? Remember the old picture books that used to tell you what color to use and where so the final pic looked right? Same idea here. A tile map is essentially paint by the number, just like a bitmap. In fact the ONLY difference between a tile map and a bitmap is that in a bitmap, 1 'tile' = 1 pixel and in a tile map '1 tile' = many pixels.

    So how do you represent a 32-bit bitmap in memory?

    Code:
    struct RGBA
    {
      BYTE red;
      BYTE green;
      BYTE blue;
      BYTE alpha;
    };
    
    RGBA *Bitmap=new RGBA[size];
    Or you can just use a DWORD or UINT.

    So what is the bitmap? A bunch of numbers that represent colors. What is a tile map? A bunch of numbers that represent tiles which are bitmaps.

    So 1 tile does not need ANY image information. It just needs to know it's ID number. Store the image data in memory somewhere and then access it and use it when you need it. So now you can use the data 50 million times, yet only have 1 instance and 1 copy of it in memory.

    Code:
    class Texture
    {
      DWORD dwID;
      DWORD *m_pBitmap;
      ...
    };
    
    class TextureContainer
    {
      std::vector<Texture *> m_vTextures;
      ...
    };
    
    class TileMap
    {
      DWORD *m_pMap;
      TextureContainer *m_pTexContainerPtr;
      ...
    };
    So if the map at some offset reads 1000, we ask the TextureContainer for the data to the bitmap with an ID of 1000. It is usually easiest and fastest if the ID simply represents the objects index in the container vector/array.

    This same principle can be applied to almost every object in your game. With this method, the graphic data is isolated to one class and the other classes which need it can then concentrate more on how they are going to use it.

    For instance an animation now simply comes down to sequencing through stored IDs using a stored frame duration for each one. The render class simply renders the current ID by asking for the bitmap data from the TextureContainer. No bitmap data is stored in the animation class.

    The following code is very long and you should ignore the MFC/Win32 specific functions and calls. It should give you an idea of how IDs work. This is an animation system from the editor I'm working on.

    CAnimSystem.h
    Code:
    #ifndef CANIMSYSTEM
    #define CANIMSYSTEM
    
    
    #include "CTileSystem.h"
    //#include "MainFrm.h"
    #include <mmsystem.h>
    #include <vector>
    
    
    class CAnimSequence;
    class CAnimSequenceContainer;
    
    class CAnimFrame
    {
      friend class CAnimSequence;
      DWORD m_dwTileID;
      float m_fFrameDuration;
      float m_fXOffset;
      float m_fYOffset;
      
    
    
      public:
        CAnimFrame(void):m_dwTileID(0),m_fFrameDuration(0.0f),
                         m_fXOffset(0.0f),m_fYOffset(0.0f) {}
    
        CAnimFrame(const CAnimFrame &fr)
        {
          m_dwTileID=fr.m_dwTileID;
          m_fFrameDuration=fr.m_fFrameDuration;
          m_fXOffset=fr.m_fXOffset;
          m_fYOffset=fr.m_fYOffset;
    
    
        }
    
        void Create(DWORD dwID,float fFrameDuration,float xo,float yo)
        {
          m_dwTileID=dwID;
          m_fFrameDuration=fFrameDuration;
          m_fXOffset=xo;
          m_fYOffset=yo;
    
        }
    
        void DoArchive(CArchive &ar)
        {
          if (ar.IsStoring())
          {
            ar << m_dwTileID;
            ar << m_fFrameDuration;
            ar << m_fXOffset;
            ar << m_fYOffset;
          }
          else
          {
            ar >> m_dwTileID;
            ar >> m_fFrameDuration;
            ar >> m_fXOffset;
            ar >> m_fYOffset;
          }
        }
            
    
    };
    
    class CAnimSequence
    {
      friend class CAnimSequenceContainer;
    
      std::vector<CAnimFrame *> AnimFrames;
      
      CWnd *m_pWindow;
    
      DWORD m_dwSize;
    
      CPoint m_Position;
      DWORD m_dwCurFrame;
      float m_fAccumTime;
    
      CTileManager *m_pTileManager;
      bool m_bLooping;
    
    
      CWinThread *m_pThread;
      
      CRect m_rectClient;
      int m_iScrollX;
      int m_iScrollY;
      bool m_bUpdatePos;
    
      float m_fCurTime;
      float m_fLastTime;
    
      bool  m_bActive;
      bool  m_bVisible;
      bool  m_bThreadTerminated;
    
      public:
        CAnimSequence(void):m_dwCurFrame(0),m_Position(0,0),m_bLooping(false),
                            m_fAccumTime(0.0f),m_pTileManager(NULL),m_iScrollX(0),
                            m_iScrollY(0),m_bUpdatePos(false),m_pThread(NULL),
                            m_fCurTime(0.0f),m_fLastTime(0.0f),m_bActive(false) {}
    
        virtual ~CAnimSequence(void)
        {
          
          if (AnimFrames.size()>0)
          {
            for(DWORD i=0;i<AnimFrames.size();i++)
            {
              if (AnimFrames[i])
              {
              
                delete AnimFrames[i];
              }
            }
    
            if (m_pThread) 
            {
              WaitForSingleObject(m_pThread->m_hThread,INFINITE);
              delete m_pThread;
            }
    
    
            AnimFrames.clear();
          }
    
          
        }
    
        float GetCurFrameDuration(void)
        {
          if (AnimFrames.size()>0)
          {
            return AnimFrames[m_dwCurFrame]->m_fFrameDuration;
          } 
          else return(0.0f);
    
        
        }
    
        DWORD GetID(DWORD dwID)
        {
          if (dwID<AnimFrames.size())
          {
            return AnimFrames[dwID]->m_dwTileID;
          } else return 0xFFFFFFFF;
        }
    
        void SetXYOffsets(DWORD dwID,float xo,float yo)
        {
          if (dwID<AnimFrames.size())
          {
            AnimFrames[dwID]->m_fXOffset=xo;
            AnimFrames[dwID]->m_fYOffset=yo;
          }
        }
    
        void GetXYOffsets(DWORD dwID,float &xo,float &yo)
        {
          if (dwID<AnimFrames.size())
          {
            xo=AnimFrames[dwID]->m_fXOffset;
            yo=AnimFrames[dwID]->m_fYOffset;
          }
    
        }
    
        void SetFrameDuration(DWORD dwID,float fDuration)
        {
          if (dwID<AnimFrames.size())
          {
            AnimFrames[dwID]->m_fFrameDuration=fDuration;
          }
        
        }
    
        float GetFrameDuration(DWORD dwID)
        {
          if (dwID<AnimFrames.size())
          {
            return AnimFrames[dwID]->m_fFrameDuration;
          } else return (0.0f);
        }
    
        void Create(CWnd *ptrWin,
                    DWORD dwSize,
                    CPoint pos,
                    CTileManager *ptrTileManager,
                    bool bLooping=true,
                    bool bUpdatePos=false)
        {
          m_pWindow=ptrWin;
          m_dwSize=dwSize;
          ptrWin->GetClientRect(&m_rectClient);
          m_iScrollX=0;
          m_iScrollY=0;
          m_bUpdatePos=bUpdatePos;
          m_Position.x=pos.x;
          m_Position.y=pos.y;
          m_dwCurFrame=0;
          m_pTileManager=ptrTileManager;
          m_bLooping=bLooping;
          m_fAccumTime=0.0f;
          m_fLastTime=(float)::timeGetTime();
          m_pThread=AfxBeginThread(DoThreading,this,THREAD_PRIORITY_IDLE);
          m_pThread->m_bAutoDelete=false;
    
          m_bVisible=true;
          m_bActive=true;
          m_bThreadTerminated=false;
    
          
         
        }
    
        void AddFrame(DWORD dwID,float fFrameDuration,float xo,float yo)
        {
          CAnimFrame *frame=new CAnimFrame();
          frame->Create(dwID,fFrameDuration,xo,yo);
          AnimFrames.push_back(frame);
        }
    
        void SetFrameData(DWORD dwID,float fFrameDuration,float xo,float yo)
        {
          if (dwID<AnimFrames.size())
          {
            AnimFrames[dwID]->m_fFrameDuration=fFrameDuration;
            AnimFrames[dwID]->m_fXOffset=xo;
            AnimFrames[dwID]->m_fYOffset=yo;
          }
        }
        bool SetCurFrame(DWORD dwID)
        {
          if (dwID<AnimFrames.size())
          {
            m_dwCurFrame=dwID;
            return false;
          } else return true;
        }
    
        void Reset(void) {m_dwCurFrame=0,m_fAccumTime=0.0f;}
        void Update(float fTimeDelta);
        void Draw(void);
        void SetVisible(bool bCond=true)
        {
          m_bVisible=bCond;
        }
    
    
        static UINT DoThreading(LPVOID p)
        {
          CAnimSequence *ptrSeq=(CAnimSequence *)p;
          
          while (ptrSeq->m_bActive)
          {
              
            if (ptrSeq->m_pWindow!=NULL)
            {
    
              if (ptrSeq->m_bVisible)
              {
            
                ptrSeq->m_fCurTime=(float)::timeGetTime();
                float fElapsed=ptrSeq->m_fCurTime-ptrSeq->m_fLastTime;
           
                 float fPause=ptrSeq->GetCurFrameDuration();
                 ::Sleep(ptrSeq->GetCurFrameDuration()*.75f);
                 ptrSeq->Update(fElapsed);
              
                ptrSeq->m_fLastTime=ptrSeq->m_fCurTime;
              }
           
            }
          }
    
          ptrSeq->m_bThreadTerminated=true;
    
          return (0);              
        }
       
        
    };
    
    
    class CAnimSequenceContainer
    {
      std::vector<CAnimSequence *> AnimSeqs;
      float m_fCurTime;
      float m_fLastTime;
    
      bool m_bActive;
      
    
     public:
      CAnimSequenceContainer(void) {}
      virtual ~CAnimSequenceContainer(void)
      {
        //Terminate threads
          
        if (AnimSeqs.size()>0)
        {
          for (DWORD i=0;i<AnimSeqs.size();i++)
          {
            AnimSeqs[i]->m_bActive=false;
            if (AnimSeqs[i]) delete AnimSeqs[i];
          }
          AnimSeqs.clear();
        }
    
    
      }
      
      bool AddFrame(DWORD dwID,DWORD dwTileID,float fFrameDuration,float xo,float yo)
      {
          if (dwID<AnimSeqs.size())
          {
    
          
            AnimSeqs[dwID]->AddFrame(dwTileID,fFrameDuration,xo,yo);
            return false;
          } else return true;
      }
    
      
    
      void Create(void)
      {
        m_bActive=true;
        
      }
      
      DWORD AddSeq(CAnimSequence *ptrSeq)
      {
        
        AnimSeqs.push_back(ptrSeq);
        
    
        return AnimSeqs.size()-1;
    
      }
    
      void ReplaceSeq(DWORD ID,CAnimSequence *ptrSeq)
      {
      }
      
    
      void Update(float fTimeDelta)
      {
        for (DWORD i=0;i<AnimSeqs.size();i++)
        {
          AnimSeqs[i]->Update(fTimeDelta);
          
        }
      }
    
      void DrawAll(void)
      {
        for (DWORD i=0;i<AnimSeqs.size();i++)
        {
          AnimSeqs[i]->Draw();
          
        }
      }
    
      void DrawSeq(DWORD dwID)
      {
        if (dwID<AnimSeqs.size())
        {
          AnimSeqs[dwID]->Draw();
        }
      }
    
      CAnimSequence GetSeq(DWORD dwID)
      {
        if (dwID<AnimSeqs.size())
        {
          return *AnimSeqs[dwID];
        }
      }
    
      float GetFrameDuration(DWORD dwSeqID,DWORD dwFrameID)
      {
        if (dwSeqID<AnimSeqs.size())
        {
          return AnimSeqs[dwSeqID]->GetFrameDuration(dwFrameID);
        } else return 0.0f;
      }
    
      void GetXYOffsets(DWORD dwSeqID,DWORD dwFrameID,float &xo,float &yo)
      {
        if (dwSeqID<AnimSeqs.size())
        {
          AnimSeqs[dwSeqID]->GetXYOffsets(dwFrameID,xo,yo);
    
    
        }
      }
    
      DWORD GetID(DWORD dwSeqID,DWORD dwFrameID)
      {
        if (dwSeqID<AnimSeqs.size())
        {
          return AnimSeqs[dwSeqID]->GetID(dwFrameID);
        } else return 0xFFFFFFFF;
      }
    
      
    };
    
    
    #endif
    CAnimSystem.cpp
    Code:
    #include <stdafx.h>
    #include "CAnimSystem.h"
    #include "MainFrm.h"
    
    
    
    
    void CAnimSequence::Update(float fTimeDelta)
    {
      //m_pCDC=m_pWindow->GetDC();
    
      if (AnimFrames.size()>0)
      {
        m_fAccumTime+=fTimeDelta;
    
        if (m_fAccumTime>=AnimFrames[m_dwCurFrame]->m_fFrameDuration)
        {
          Draw();
          m_fAccumTime=0.0f;
          m_dwCurFrame++;
          if (m_dwCurFrame>AnimFrames.size()-1)
          {
            m_dwCurFrame=0;
            m_fAccumTime=0.0f;
          }
        }
      }
    
    }
    
    void CAnimSequence::Draw(void)
    {
      //Draw to pDC with TileDC
    
      if (AnimFrames.size()>0)
      {
        
          DWORD dwTileID=AnimFrames[m_dwCurFrame]->m_dwTileID;
    
        
    
          CDC *TileDC=m_pTileManager->GetDC(dwTileID);
       
          int ScreenX=m_Position.x+AnimFrames[m_dwCurFrame]->m_fXOffset;
          int ScreenY=m_Position.y+AnimFrames[m_dwCurFrame]->m_fYOffset;
        
          if (m_bUpdatePos)
          {
            m_Position.x=ScreenX;
            m_Position.y=ScreenY;
          }
    
    
          ScreenX+=m_iScrollX;
          ScreenY+=m_iScrollY;
        
         
          if (ScreenX>m_rectClient.left  && 
              ScreenX<m_rectClient.right && 
              ScreenY>m_rectClient.top   &&
              ScreenY<m_rectClient.bottom)
          {
         
              //DestDC->TextOut(ScreenX,ScreenY,"........ you");
            CClientDC dc(m_pWindow);
            
            dc.BitBlt(ScreenX,ScreenY,
                      m_dwSize,
                      m_dwSize,
                      TileDC,
                      0,0,
                      SRCCOPY);
    
            m_pWindow->ReleaseDC(TileDC);
     
    
          }
      
          
        
      }
    }
    I really can't explain it more than that. I did not research this system, I just started using it because it made sense and it was very simple. Little did I know that books I would read in the future as well as the DX SDK talk about a similar type of system. When you code for games long enough you begin to realize there is a really good way to do stuff and a really bad way. A lot of it comes through trial and error and realizing......man that system sucks, or man this system is easy.

    You will develop your own and hand-tune it to your needs.
    This is not the only way to go about things.
    Last edited by VirtualAce; 01-11-2006 at 02:10 AM.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,892
    Have you read the Game Programming Gems books? Some of the articles are about resource management systems such as this. GPG1, for example, contains the article "A Generic Handle-Based Resource Manager". GPG3 has "Handle-Based Smart Pointers", which you can use to simplify usage of the thing.

    Bubba, I think you should replace 0xFFFFFFFF by some named constant.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,586
    Bubba, I think you should replace 0xFFFFFFFF by some named constant.
    Thought about that as well. I just figured that no one would ever have over 4,194.... tiles in memory.

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,892
    It's not that. It's just easier to type INVALID_ID instead of counting Fs, and it's clearer, too.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,586
    True. I don't like functions relying on a special value, but I wasn't sure how to go about it. It is a DWORD return type so -1 is 0xFFFFFFFF but returning -1 in a DWORD return type function didn't look quite right.

    Any ideas on how I can change this?

    Code:
    bool GetID(DWORD dwSeqID,DWORD dwFrameID,DWORD &dwOutID)
      {
        if (dwSeqID<AnimSeqs.size())
        {
          dwOutID=AnimSeqs[dwSeqID]->GetID(dwFrameID);
          return false;
        } else return true;
      }
    Ya like?

    GetID might seem redundant but there are reasons for it. The ID's assigned by the editor are stored on disk in the main file and the actual ID or index of the object in memory is also stored.

    The reason for this is that ID's must be assigned in the editor to represent the index in the vector/array, but these IDs cannot be placed into the map. This is because the engine would then have to load the bitmaps in the exact same order as the editor. This would require either a naming convention that used values in the filename or it would require a game def file that showed the order. I opted for something else.

    At load time, the engine knows how many bitmaps are in the data file. It creates an array and then proceeds to load. As it loads it uses the ResourceID as the index into the engine array. In this way, the final engine array should exactly match the editor array. However, the editor need not do this when it loads resource files. It can simply load and as long as it places the ResourceID into the map instead of the ID of the bitmap in the current editor vector, it will work. The IDs will match.

    Using these methods requires no look ups and I don't have to use any of the STL find() functions on my container in the editor.
    It took some time to come up with this system, but it seems to work ok.
    Last edited by VirtualAce; 01-11-2006 at 10:23 AM.

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,892
    How about boost::optional<DWORD>? Too expensive? (I believe the overhead is minimal.)

    I'm not a fan of out parameters.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,586
    Neither am I. Normally I provide two styles of functions in my classes. One's that return values in registers (hence EAX, ST(0)) and one's that return it on the stack. I prefer returning in registers.

  13. #13
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    964
    Gimme a couple days and I might have something more to show, thanks for all the help..

    I have the idea of how the whole thing works as a system, but the only questions I have now are about specific functions.

    I'm gonna psuedocode what I think the register function will do

    Code:
    void RegisterObject(Object)
    {
    
    check if object is already registered
    if so create an objectinfo instance and fill out the correct stuff (objectID, starting index, ending index)
    
    if not generate an ID
    check to see if data for that object exists already
    if so get the starting index and ending index of the data
    create a new objectinfo instance and fill out the correct stuff
    
    }
    Is that basically right?

    Here is what I have so far, and so far it is coming together in my head, now I finally realize how the data structures are connected to the containers are connected to the objectInfo struct, they all have object ID's!

    Code:
    #include "windows.h"
    
    struct MaterialData
    {
    	DWORD dwID
    	float Ambient[4], Diffuse[4], Specular[4], Emissive[4];
    	float Shininess;
    	int Texture;
    	char *TextureFilename;
    };
    
    class MaterialContainer
    {
    	std::vector<MaterialData *> Matertials;
    };
    
    struct PhysicsData
    {
    	DWORD dwID
    	double mass;
    	double velocity[3];
    };
    
    class PhysicsContainer
    {
    	std::vector<PhysicsData *> Physics;
    };
    
    struct VertexData
    {
    	DWORD dwID
    	float vertex[3];
    	char boneID;
    };
    
    class VertexContainer
    {
    	std::vector<VertexData *> Vertices;
    };
    
    struct PositionData
    {
    	DWORD dwID
    	float pos[3];
    };
    
    class PositionContainer
    {
    	std::vector<PositionData *> Positions;
    };
    
    struct ObjectInfo
    {
    	DWORD dwStartIndex;
    	DWORD dwEndIndex;
    	DWORD dwID;
    	DWORD *dwTextureIDs;
    };
    
    class ResourceCache
    {
    	PhysicsContainer   Physics;
    	VertexContainer    Vertices;
    	PositionContainer  Positions;
    	MaterialContainer  Materials;
    };
    
    class RenderList
    {
    	ResourceCache *RenderData;
    	std::vector<DWORD> ObjectIDs;
    };
    Last edited by Shamino; 01-11-2006 at 11:12 AM.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  14. #14
    Dae
    Dae is offline
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    I didn't mean Bubba didn't explain it well. I meant you should have some knowledge on the subject before expecting a light to turn on, hence the point on tutorials. Think about when you read up on something but aren't sure how to use it..

    Whats RegisterObject() for? For the class that handles all objects? But in that case you're using that object manager to create the objects, so there would be no external creation of objects and therefor no need for a register object method, or a check to see if it exists already. In the method that creates an object in your object manager you would find a start/end index, id, and pass those on construction of the object, to be assigned to ObjectInfo.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  15. #15
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,586
    There is nothing more I can contribute to this thread. Shamino if you want to learn the system or learn to develop your own I extend an offer for you to come on board and help with our current project.

    You have enough knowledge, enough desire, and enough dedication to game code to be an asset to us.

    PM me and let me know. We usually meet on Teamspeak so it would be good if you had a microphone and speakers. This allows us to talk one on one and is about ten million times better than using MSN or some other chat program.

    If you do join, I'm sure you will see there is nothing uber about our system and you could easily understand it.

Page 1 of 3 123 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. VC++ 6 Menu resource question
    By Mecnels in forum Windows Programming
    Replies: 1
    Last Post: 04-22-2003, 12:04 PM
  2. JPEG Resource
    By dragunsflame in forum Windows Programming
    Replies: 2
    Last Post: 04-08-2003, 10:07 PM
  3. resource problem/question
    By stallion in forum Windows Programming
    Replies: 4
    Last Post: 01-29-2003, 01:08 PM
  4. menu resource file
    By satriani in forum Windows Programming
    Replies: 5
    Last Post: 06-08-2002, 10:52 PM
  5. Radiohead rule
    By Brian in forum A Brief History of Cprogramming.com
    Replies: 2
    Last Post: 01-18-2002, 06:37 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21