Forum

Minimal Standalone Cvar System

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Minimal Standalone Cvar System

Postby Baker » Sat Jan 21, 2012 4:18 am

Short and to the point. Standalone (well ... 2 or 3 tiny little things ... but eventually I'll post a full standalone with an #ifdef or something).

[Not meant for a Quake engine ... just a simple key/value system supporting floats and strings.]

Code: Select all
/***********************************************************************************************************\
*****
**
**   cvars.c:   Minimal cvar system.  Case sensitive.  Use Cvars_GetString ("fov") or Cvars_GetFloat ("fov")
**            Requires Cvars_Startup () to initialize; Cvars_Shutdown to shut down.
*****
\***********************************************************************************************************/

#include "engine.h"
#include "cvars.h"

extern cvars_t cvars[];
extern const int numcvars;

static cvars_t* first_console_variable;

///////////////////////////////////////////////////////////////////////////////
//
//   Private functions. 
//
///////////////////////////////////////////////////////////////////////////////

// Clearinghouse for all value changes.  Sets the string and evaluates the float value.

static void sCvar_ChangeValue (cvars_t* myCvar, const char* newString)
{
   // If already allocated ... free old pointer
   if (myCvar->string) Memory_free (myCvar->string);
   
   myCvar->string   = Memory_strdup (newString, myCvar->key);
   myCvar->value   = atof(newString);
}

static cvars_t* sCvar_FindForName (const char* cvarname)
{
   for (int i = 0; i < numcvars; i++)
   {
      if (StringMatch(cvars[i].key, cvarname))
         return &cvars[i];
   }
   
   return NULL; // As a private function we do not error out
}

///////////////////////////////////////////////////////////////////////////////
//
//   Get values.
//
///////////////////////////////////////////////////////////////////////////////


// Convert float to string and set.
float Cvars_GetFloat (const char* cvarname)
{
   cvars_t* cvar_to_get = sCvar_FindForName(cvarname);
   
   if (cvar_to_get)   return cvar_to_get->value;
   else            Console_Printf ("Cvars_GetFloat cvar \"%s\" not found", cvarname);
   
   return 0.0;
}

// Just pass the string along after determining which cvar it is.
const char* Cvars_GetString (const char* cvarname)
{
   cvars_t* cvar_to_get = sCvar_FindForName(cvarname);
   
   if (cvar_to_get)   return cvar_to_get->string;
   else            Console_Printf ("Cvars_GetString cvar \"%s\" not found", cvarname);
   
   return NULL;
}

///////////////////////////////////////////////////////////////////////////////
//
//   Managed value change functions.
//
///////////////////////////////////////////////////////////////////////////////

// Convert float to string and set.
void Cvars_SetFloat (const char* cvarname, const float newValue)
{
   Cvars_SetString (cvarname, StringTemp_NiceFloatString (newValue));
   // Cvars_SetString handles error messages so no need for us to do it.
}

// Just pass the string along after determining which cvar it is.
void Cvars_SetString (const char* cvarname, const char* newString)
{
   cvars_t* cvar_to_get = sCvar_FindForName(cvarname);
   
   if (cvar_to_get)   { sCvar_ChangeValue (cvar_to_get, newString); }
   else            Console_Printf ("Cvars_SetString cvar \"%s\" not found", cvarname);

}

///////////////////////////////////////////////////////////////////////////////
//
//   Startup and shutdown functions.
//
///////////////////////////////////////////////////////////////////////////////

static fbool sCvars_Alphabetical_List_Add (cvars_t *newVariable)
{
#if 0
   const cvars_t   *existing = sCvar_FindForName (newVariable->key);
   
   // prevent duplicates
   if (existing)      { Console_Printf ("Cannot add variable \"%s\" because it already exists", newVariable->key); return False; }
#endif
   
   // alphabetic linking
   do
   {
      if (first_console_variable && String_isLowerAlphabetically (newVariable->key, first_console_variable->key))
      {
         // Locate insertion point
         cvars_t   *previous   = first_console_variable;
         cvars_t   *cursor      = first_console_variable->next_alphabetically;
         
         // Starting at the top, run through the list locating the first variable alphabetically lower than our new one (or stop at end)
         while (cursor && String_isLowerAlphabetically (newVariable->key, cursor->key))
         {
            previous         = cursor;
            cursor            = cursor->next_alphabetically;
         }
         
         newVariable->next_alphabetically= previous->next_alphabetically;   // Our next variable alphabetically is the "next" variable for the previous one
         previous->next_alphabetically   = newVariable;                  // And update the previous variable's "next" to be us
         
         break;   // Done ... get out
      }
      
      // If we are here, we are the first of the list
      
      newVariable->next_alphabetically   = first_console_variable;
      first_console_variable            = newVariable;
      
   } while (0);
   
   return True;
}
   
// Copy default string to string value and evaluate float by calling SetString
void Cvars_Startup (void)
{
   for (int i=0; i < numcvars; i++)
      if (sCvars_Alphabetical_List_Add (&cvars[i])) // Checks for dups
         sCvar_ChangeValue (&cvars[i], cvars[i].default_string);
}

// Free any outstanding memory allocations.
void Cvars_Shutdown (void)
{
   for (int i=0; i < numcvars; i++)
      if (cvars[i].string) Memory_free (cvars[i].string);
   
}


But of course this leads to "well kinda of useless without a console". Largely true. I might do a standalone console with minimal "command" interpreter too (just enough to interact with cvars). I kind of need a plug-in console that can use a fixed font deal (so yeah ... I have to do rendering it on-screen too and that means loading something like a MoonDrunk font.). I've been playing with some stuff and without the convenient on-the-fly ability to change stuff, a lot of experimentation is lame.

[Thing might see updates as I add some features or find something broke or whatever.]
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Minimal Standalone Cvar System

Postby Baker » Sat Jan 21, 2012 3:54 pm

Kinda of weird how if you mess with the engine stuff long enough, you get some crazy ideas and start typing in stuff ...

Code: Select all
/***********************************************************************************************************\
*****
**
**   bsp_data.c:   Think of this as a .bsp or .map file
**
** Knows what an entity is.
** Knows what a texture is.
** Knows what a key/action is.
**
*****
\***********************************************************************************************************/

#include "engine.h"
#include "cvars.h"      // This file must know what a cvar is to function.
#include "entities.h"   // By nature we must know about entities
#include "controls.h"   // Has to know this

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Keys and such
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

keybinds_t mykeys [] =
{
   { 'W',      ACTION_CAMERA_FORWARD         },
   { 'A',      ACTION_CAMERA_LEFT            },
   { 'S',      ACTION_CAMERA_BACKWARD         },
   { 'D',      ACTION_CAMERA_RIGHT            },
   { 'Q',      ACTION_CAMERA_UP            },
   { 'E',      ACTION_CAMERA_DOWN            },   
   { '.',      ACTION_CAMERA_PITCH_DOWN      },   
   { '/',      ACTION_CAMERA_PITCH_UP         },   
   { '[',      ACTION_CAMERA_ROLL_CLOCKWISE   },   
   { ']',      ACTION_CAMERA_ROLL_CCW         },      
   
   { LEFTARW,   ACTION_CAMERA_YAW_LEFT         },   
   { RIGHTARW,   ACTION_CAMERA_YAW_RIGHT         },   
   { UPARW,   ACTION_CAMERA_FORWARD         },   
   { DOWNARW,   ACTION_CAMERA_BACKWARD         },      
   
   { 'P',      ACTION_TOGGLE_PAUSE            },      
   { MOUSE01,   ACTION_SELECT_OBJECT         },      
};

const int nummyKeys = sizeof(mykeys) / sizeof(mykeys[0]);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Data
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define NO_PARENT NULL  // For readability here ...
#define UNNAMED   NULL  // For readability here ...
#define NO_TEXT     NULL  // For readability here ...

cvars_t cvars[] =
{
   {"fov_x",      "90"         },
   {"fov_y",      "0"            },   
   {"aspect",      "1.33333"      },
   {"znear",      "0.1"         },
   {"zfar",      "1000"         },
   {"title",      "Test World 1"   },   
   {"skybox",      "whatever"      },
   {"movespeed",   "21"         },
   {"anglespeed",   "100"         },
};

const int numcvars = sizeof(cvars) / sizeof (cvars[0]);


static entity_t myStartEntities [] =
{
//                                                    |-------------------------------------------------------------------------|
//                                                      fields are grouped as we may support cyclical alternators in the future    AND NOT in way
//                                                      bobbing        rotating            pulsating          glowing    fading    that avelocity would help
//                                                    |-------------------------------------------------------------------------|
//     name      parentname  texture      model         origin          angles            dimensions        color in rgb  alpha    avelocity
   {"player",   NO_PARENT,  NO_TEXT   , MODEL_NONE, {  0,  0,  0 }, {  0, 90,  0 }, {  1.0,  1.0,  1.0 }, {  1,  1,  1 }, 1.0,   {  0,  0,  0 } },     
   {UNNAMED,    NO_PARENT, "crosshair", MODEL_CUBE, { 10,  5,  1 }, {  0,  0,  0 }, {  1.0,  1.0,  1.0 }, {  0,  0,  9 }, 1.0,   {  9,  0,  0 } },
   
   {UNNAMED,    NO_PARENT, "sthelens",  MODEL_CUBE, {  0,  5,  1 }, {  0,  0,  0 }, {  0.9,  0.1,  0.6 }, {  1,  1,  1 }, 0.9,   {  0,  9,  0 } },
   {"Center",   NO_PARENT, "voyager",   MODEL_FACE, { -1,  5,  1 }, {  0,  0,  0 }, {  0.9,  0.1,  0.6 }, {  1,  1,  1 }, 1.0,   {  0,  0,  9 } },
   {UNNAMED,    NO_PARENT, "library",   MODEL_CUBE, {  1,  5,  0 }, {  0,  0,  0 }, {  0.9,  0.1,  0.6 }, {  1,  1,  1 }, 1.0,   {  0,  0,  0 } },
   {UNNAMED,    "Center",  "radiation", MODEL_CUBE, {  0, -2,  0 }, {  0,  0,  0 }, {  0.9,  0.1,  0.6 }, {  1,  1,  1 }, 1.0,   {  0,  0,  0 } },
};


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Data
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// This structure lets this function ONLY be called
// While allowing us to populate the world entities however we wish including calculating them
typedef void (*entityLoader_t) (const entity_t* myEnt);
void BSP_LoadEntities_Limited_Access (entityLoader_t myEntityLoaderFunc);

void BSP_LoadEntities_Limited_Access (entityLoader_t myEntityLoaderFunc)
{
   const int entities_count = sizeof(myStartEntities) / sizeof(myStartEntities[0]);   
   
   for (int i = 0; i < entities_count; i++)
   {
      const entity_t*   ent = &myStartEntities[i];
      
      myEntityLoaderFunc (ent);
   }
   
}


And stuff ...

Code: Select all
/***********************************************************************************************************\
 *****
 **
 **   entities.c:   Add entities, physics, etc.
 **
 *****
 \***********************************************************************************************************/


#include "engine.h"
#include "entities.h"   // Must know about entities here obviously
#include "glcommon.h"
#include "controls.h"

entity_t* Entity_Raw_Format_To_Defaults (entity_t* myEnt);
void Entities_RenderScene (void);
void Entity_Action (const enum navigations whichAction);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Arrays: No other file gets access to these directly
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define MAX_ENTITIES 200

entity_t      Entities[MAX_ENTITIES];
int            numentities = 0;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Externally available only to bsp_data.c
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////


// Format entity to stuff that better not default to 0 or NULL
entity_t* Entity_Raw_Format_To_Defaults (entity_t* myEnt)
{
   memset (myEnt, 0, sizeof (*myEnt));               // Zero fill it
   
   // Then deal with stuff that non-zero default == bad!
   VectorSet (myEnt->dimensions,   1,  1,  1);         // Size of 1
   VectorSet (myEnt->rgb,          1,  1,  1);         // White
   myEnt->alpha      = 1;                     // Fully opaque
   
   return myEnt;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Externally available only to bsp_data.c by passing the function on.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static entity_t* Entity_FindForName (const char* searchname);

int GL_Texture_Add_To_List (const char* newTextureName);

static void sEntity_Add (const entity_t* NewEntity)
{
   if (numentities + 1 > MAX_ENTITIES) Host_FatalError ("No more free entities.  Max = %i", MAX_ENTITIES);
   
   {
      int newindex = numentities++;
      
      memcpy (&Entities[newindex], NewEntity, sizeof(Entities[0]));   
      {
         entity_t* newent   = &Entities[newindex];
         
// Fill in basics         
         newent->status      = STATUS_PRESENT;
         newent->index      = newindex;
         newent->Parent      = Entity_FindForName (newent->parentname);
         newent->TexIndex   = GL_Texture_Add_To_List (newent->texturename);
         newent->selfrandom   = myRandomNumber();

      }
   }
   
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Find an entity by name
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static entity_t* Entity_FindForName (const char* searchname)
{
   if (searchname == NULL) return NULL;   // No name to search for
   
   for (int i = 0; i < numentities; i++)
      if (Entities[i].name && StringMatch (Entities[i].name, searchname))
         return &Entities[i];
   
   Host_FatalError ("Failed to resolve entity named %s", searchname);
   return NULL; // Failed search
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Entity placement functions.  Place and unplace.  Using archaic OpenGL 1.x push/pop
//   To do: Generate individual matrices and use glMulMatrix.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int sEntity_RecursiveParent_Place (const entity_t* parentEnt)
{
   static int recursivetimes = 0;

   if (++recursivetimes > 5)  Host_FatalError ("Max recursions hit");

   if (parentEnt->Parent) sEntity_RecursiveParent_Place (parentEnt->Parent);
   
   glPushMatrix ();
   GL_PlaceModel         (parentEnt->origin, parentEnt->angles, parentEnt->dimensions);
   
   recursivetimes --;
   
   return recursivetimes; // Better equal 0 when done
}

static int sEntity_RecursiveParent_UnPlace (const entity_t* parentEnt)
{
   static int recursivetimes = 0;
   
   if (++recursivetimes > 5)  Host_FatalError ("Max recursions hit");
   
   if (parentEnt->Parent)      sEntity_RecursiveParent_UnPlace (parentEnt->Parent);
   
   glPopMatrix      ();
   
   recursivetimes --;
   
   return recursivetimes; // Better equal 0 when done
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Run physics for entity or entities.
//   Go down list of entities, if parent run parent physics first, then run your physics.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void Entities_Physics_For_Ent (entity_t *ent);
void Entities_RunPhysics (const float elapsedFrameTime);

static float elapsedTime = 0;
void Entities_RunPhysics (const float elapsedFrameTime)
{
   // Set frame time
   elapsedTime = elapsedFrameTime;

   // Wipe physics.  Sure we could use a framecounter, but that's tacky
   for (int i = 0; i < numentities; i++)
      Entities[i].isPhysicsUpdated = 0;
   
   // Run physics
   for (int i = 0; i < numentities; i++)
      Entities_Physics_For_Ent (&Entities[i]);

   // Physics completed
}

static void Entities_Physics_For_Ent (entity_t *ent)
{
   if (ent->isPhysicsUpdated)   return;                     // Already ran physics
   if (ent->Parent) Entities_Physics_For_Ent (ent->Parent);   // Run Parent's physics first
   
   // Actual running of physics here ...
   
   // Angular velocity
   for (int j = 0; j < 3; j++)
      if (ent->angularvelocity[j]) ent->angles[j] += ent->angularvelocity[j] * elapsedTime;
   
   // Indicate that physics are updated for this frame
   ent->isPhysicsUpdated = True;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Rendering and the scene.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

GLuint GL_TextureSlot_For_Index (const int texindex);

static void sRenderEntity (const entity_t * ent)
{
   if (ent->texturename)         
      glBindTexture   (GL_TEXTURE_2D, GL_TextureSlot_For_Index(ent->TexIndex) );   // Select our texture
   else                  glDisable      (GL_TEXTURE_2D);
   
   if (ent->alpha < 1)       {   glDisable      (GL_DEPTH_TEST);
                        glEnable      (GL_BLEND); }
   
   glColor4f (ent->rgb[0], ent->rgb[1], ent->rgb[2], ent->alpha);
      
   glPushMatrix();      // Store the matrix for restoration after we render entity ...
   {
      if (ent->Parent)
         sEntity_RecursiveParent_Place (ent->Parent);
      
      // World Space (World Matrix) to Model Space
      GL_PlaceModel         (ent->origin, ent->angles, ent->dimensions);      
      
      switch (ent->model)
      {            
//         case MODEL_FACE:            GL_ModelRenderFace         ();            break;
         case MODEL_CUBE:            {
            GL_ModelRenderCube_TexScale ();            
//            const tex2_t myScale = {1.0, 1.0};
//            GL_ModelRenderCube_TexScale   (myScale);      break;
//            GL_Model_Cube ();
         }
         default:                  break;
      }
      
      if (ent->Parent) // Pop again
         sEntity_RecursiveParent_UnPlace (ent->Parent);
      
   }
   glPopMatrix();      // Restore matrix to previous state
   
   
   glColor4f (1,1,1,0);   
   
   if (ent->alpha < 1)       {      glEnable      (GL_DEPTH_TEST);
                           glDisable      (GL_BLEND); }

   if (ent->texturename == NULL)   glEnable   (GL_TEXTURE_2D);
   
   
}

void Entities_RenderScene (void)
{
   // Render view entity.  Hardcoded to 0 for now ....
   {
      entity_t* viewent = &Entities[0];
      GL_PlaceCamera (viewent->origin, viewent->angles, NULL, NULL);
   }
   
   
   // Entities ... cycle through and set location and stuff
   for (int i = 1; i< numentities; i++)
   {
      entity_t* ent = &Entities[i];
      
      if (ent->model == MODEL_NONE) continue; // Do not pass go
      
      if (ent->Parent)
         sEntity_RecursiveParent_Place (ent->Parent);
      
      glPushMatrix ();
      {   // snag the contaminated matrix.
         glTranslatef (ent->origin[0], ent->origin[1], ent->origin[2]);
         glGetFloatv (GL_MODELVIEW_MATRIX, ent->modelsview); 
      } glPopMatrix();
      
      if (ent->Parent) // Pop again
         sEntity_RecursiveParent_UnPlace (ent->Parent);
      
      sRenderEntity (ent);
   }
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Action evaluator ...
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Entity_Action (const enum navigations whichAction)
{
   extern fbool Paused;
   extern float recentSlice;
   const float movespeed   = Cvars_GetFloat ("movespeed")  * recentSlice;
   const float anglespeed   = Cvars_GetFloat ("anglespeed") * recentSlice;
   
   entity_t* viewent = &Entities[0];
   
   // No actions while paused.  Except unpause obvliously
   if (Paused == True && whichAction != ACTION_TOGGLE_PAUSE) return;
   
   switch (whichAction)
   {
      case ACTION_CAMERA_FORWARD:         Origin_Move_Forward      (viewent->origin, viewent->angles,  movespeed); break;
      case ACTION_CAMERA_BACKWARD:      Origin_Move_Forward      (viewent->origin, viewent->angles, -movespeed); break;
      case ACTION_CAMERA_LEFT:         Origin_Move_Right      (viewent->origin, viewent->angles, -movespeed); break;
      case ACTION_CAMERA_RIGHT:         Origin_Move_Right      (viewent->origin, viewent->angles,  movespeed); break;
      case ACTION_CAMERA_UP:            Origin_Move_Up         (viewent->origin, viewent->angles,  movespeed); break;
      case ACTION_CAMERA_DOWN:         Origin_Move_Up         (viewent->origin, viewent->angles, -movespeed); break;
      case ACTION_CAMERA_YAW_LEFT:      Angle_Absolute_Adjust   (&viewent->angles[1],   anglespeed); break;
      case ACTION_CAMERA_YAW_RIGHT:      Angle_Absolute_Adjust   (&viewent->angles[1],  -anglespeed); break;

      case ACTION_CAMERA_PITCH_UP:      Angle_Absolute_Adjust   (&viewent->angles[0],  anglespeed); break;
      case ACTION_CAMERA_PITCH_DOWN:      Angle_Absolute_Adjust   (&viewent->angles[0], -anglespeed); break;
      case ACTION_CAMERA_ROLL_CLOCKWISE:   Angle_Absolute_Adjust   (&viewent->angles[2],  anglespeed); break;
      case ACTION_CAMERA_ROLL_CCW:      Angle_Absolute_Adjust   (&viewent->angles[2], -anglespeed); break;
//   
//      case ACTION_CAMERA_X_LEFT:         Camera_AdjustAngle   (_ANG0,  sensivitiy); break;
//      case ACTION_CAMERA_X_RIGHT:         Camera_AdjustAngle   (_ANG0,  sensivitiy); break;
//      case ACTION_CAMERA_Y_UP:         Camera_AdjustAngle   (_ANG0,  sensivitiy); break;
//      case ACTION_CAMERA_Y_DOWN:         Camera_AdjustAngle   (_ANG0,  sensivitiy); break;
//      case ACTION_CAMERA_Z_FORWARD:      Camera_AdjustAngle   (_ANG0,  sensivitiy); break;
//      case ACTION_CAMERA_Z_BACK:         Camera_AdjustAngle   (_ANG0,  sensivitiy); break;
         
      case ACTION_TOGGLE_PAUSE:         Paused = !Paused; break;
         
      default:                     break;
   }
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//   Setup the world entities.  Called by world.  Returns a pointer to the entity count for the world to read.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

typedef void (*entityLoader_t) (const entity_t* myEnt);
void BSP_LoadEntities_Limited_Access (entityLoader_t myEntityLoaderFunc);
const int* Entities_Startup_Limited_Access (void);


const int* Entities_Startup_Limited_Access (void)
{
   // Call the load function.  Pass the entity addition function.
   BSP_LoadEntities_Limited_Access (sEntity_Add);   
   
   return &numentities;
}


Probably not far from being able to load .bsp from file and then some aggravating thoughts about storing "faces" for collision (probably wouldn't be that if I wrote it up on paper first). And the last thing needed is a map editor that could turn out to be bad. And have engine stuffs to do (alpha textures thing, psp thing, ...) Probably will add selection ability, wrap it up and post it in 3D selection thread as a demo project.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest