Minimal Standalone Cvar System
Moderator: InsideQC Admins
2 posts
• Page 1 of 1
Minimal Standalone Cvar System
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.]
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.]
[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?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Minimal Standalone Cvar System
Kinda of weird how if you mess with the engine stuff long enough, you get some crazy ideas and start typing in stuff ...
And stuff ...
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.
- 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?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
2 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest