MicroG3D -- A Very Simple Application.

Here is some bare-bones code for getting the engine up and running.  This is the most straightforward code I could compose, showing the minimum code needed to look around in a world.  It was inspired by ProjectZ, so thanks again Seven.

Features:

Absent Features:

I know you are eager to get started. But first a word of caution...

Getting a first compile CAN TAKE FOREVER when you are a newbie, so please don't give up after the first try!  Many users are newcomers to C/C++ (as I was), and learning to deal with the compiler can be frustrating.  We have all been there, but remember that patience pays off.  Setting up your compiler project is critical. I use Borland's C++ Builder, but the same principles should apply with VC++.  You have to have a PROJECT FILE that includes all sorts of useful information for the compiler to use. It should include at least the following:

Then, after you get the program compiled, you are not quite ready for it to run yet. You have to make sure that your program has access to some important .dll files. You will need the Genesis.dll file in the directory where your program is running. And also the video driver files. For this demo, you will need D3DDrv.dll in the same directory (and DirectX must be installed on your computer). I then typically have two subdirectories for levels and actors. Thus my directory looks like this:

[MicroG3D]
+--[Actors]
     +--dema.act
+--[Levels]
     +--MyLevel.bsp
+--MicroG3d.exe
+--D3DDrv.dll
+--Genesis.dll
+--(my source and project files)

A few important notes:

OK, now go get started!

//---------------------------------------------------------
//Start of Code
//---------------------------------------------------------
//MicroG3D
//By Kevin Toppenberg, MD (kdtop)
//  Completed: 1-16-03
//------------------------------------------------------------------------------------------
#include <windows.h>
#include "genesis.h"
#include <assert.h>


#define APP_NAME "MicroG3d"


//--------------------------------------------------------------------
HWND GetOrMakeWindowHandle(HINSTANCE hInstance, int CmdShow, int& Width, int& Height)
{
     WNDCLASSEX wcex;
     RECT       ScreenRect;
     int        WinXPos, WinYPos;
     HWND       hWnd = 0;
     RECT       r;

     // create a nice looking window and register it
     wcex.cbSize               = sizeof(WNDCLASSEX);
     wcex.style                = CS_HREDRAW | CS_VREDRAW;
     wcex.lpfnWndProc          = DefWindowProc; //HandleMessagesProc;
     wcex.cbClsExtra           = 0;
     wcex.cbWndExtra           = 0;
     wcex.hInstance            = hInstance;
     wcex.hIcon                = NULL;
     wcex.hCursor              = NULL;
     wcex.hbrBackground        = (HBRUSH)(COLOR_WINDOW+1);
     wcex.lpszMenuName         = NULL;
     wcex.lpszClassName        = APP_NAME;
     wcex.hIconSm              = NULL;
     RegisterClassEx(&wcex);

     // store the window handle in the class variable
     hWnd = CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
                         CW_USEDEFAULT, Width, Height, NULL, NULL, hInstance, NULL);

     // if something went wrong then bail
     assert (hWnd);

     // reset the window size to what we want and center it on the screen
     GetWindowRect (GetDesktopWindow(), &ScreenRect);
     SetWindowPos (hWnd, HWND_TOP, (((ScreenRect.right + ScreenRect.left) / 2) - (Width / 2)),
                   (((ScreenRect.bottom + ScreenRect.top - 250) / 2) - (Height / 2)),
                   Width, Height, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);

     // let's get started
     ShowWindow(hWnd, SW_SHOW);
     UpdateWindow(hWnd);
     //  SetCursor (NULL);

     //Recalculate Width and Height
     GetClientRect(hWnd,&r);
     Width		= r.right;
     Height	= r.bottom;

     return (hWnd);
}

void WrapAngles (geFloat* pValue) {
  while (*pValue > 2*GE_PI) {*pValue -= 2*GE_PI;}
  while (*pValue < 0)       {*pValue += 2*GE_PI;}
}


//---------------------------------------------------------------------------
int WINAPI main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

  //Declare some more needed variables.
  typedef struct tInfo {
    geVec3d    Pos;
    geVec3d    Angles;
    geXForm3d  XForm;		// transform that will effect actor or camera movement
    geXForm3d  PoseXForm;
  } tInfo;


  geEngine*        MyEngine = NULL;	// pointer to GENESIS engine structure
  geDriver_System* MyDrvSys = NULL;	// pointer to GENESIS video driver system structure
  geDriver*        MyDriver = NULL;	// pointer to GENESIS video driver structure
  geDriver_Mode*   MyMode = NULL;	// pointer to GENESIS video mode structure
  geWorld*         MyWorld = NULL;	// pointer to GENESIS World structure
  geCamera*        MyCamera = NULL;	// pointer to GENESIS Lens structure 
  tInfo            MyCameraInfo;        // structure to hold camera position and orientation
  geVFile*         MyFileSys = NULL;	// pointer to GENESIS VFile structure 
  int              WindowWidth = 800;	// default screen width
  int              WindowHeight = 400;	// default screen height
  geRect           Rect;  		// a Rect to store the window size  
  geActor_Def*	   ActorDef;		// pointer to GENESIS actor definition structure
  geActor*         MyActor;		// pointer to GENESIS actor structure
  geFloat          ElapsedTime;         // Hold elapsed time from last frame render
  geExtBox         ExtBox;		// Holds bounding box for actor
  HWND             MyWindowHandle;      // Handle to output window

  //---------------------------------------------------------------------------------------------
  //Initialize Engine
  //---------------------------------------------------------------------------------------------

  //Function to get a handle of a display window.
  MyWindowHandle = GetOrMakeWindowHandle(hInstance, nCmdShow, WindowWidth, WindowHeight);  

  //Make a new Engine
  MyEngine = geEngine_Create(MyWindowHandle, APP_NAME, ".");  assert (MyEngine);

  //Turn off the genesis framerate counter (Optional)
  geEngine_EnableFrameRateCounter(MyEngine, GE_FALSE); 

  //Create the genesis driver system
  MyDrvSys = geEngine_GetDriverSystem(MyEngine);  assert (MyDrvSys);

  //---------------------------------------
  //Find desired driver

  const char* DriverName;
  const char* ModeName;

  //cycle through available drivers, looking for Windowed D3D driver
  do {
    MyDriver = geDriver_SystemGetNextDriver(MyDrvSys, MyDriver);
    if (!MyDriver) break;
    geDriver_GetName(MyDriver, &DriverName);  // get driver name
    //Check if this is the DirectX driver
    if (strnicmp(DriverName, "(D3D)", 5) !=0) continue;

    //Examine available modes for this driver
    do {
      MyMode = geDriver_GetNextMode(MyDriver, MyMode);
      if (!MyMode) break;
      geDriver_ModeGetName(MyMode, &ModeName);   // get mode name
      // try to find a windowed mode
      if (strnicmp(ModeName, "Window", 6)==0) break;
    } while (MyMode);  assert (MyMode);

  } while (MyDriver && !MyMode);
  assert (MyDriver && MyMode);

  //Let the genesis engine intialize with our driver
  geEngine_SetDriverAndMode(MyEngine, MyDriver, MyMode);

  // brighten things up just a bit (Optional)
  geEngine_SetGamma(MyEngine, 2.0f);

  //Now make a camera
  Rect.Left = 0; Rect.Top = 0; Rect.Right = WindowWidth-1;  Rect.Bottom = WindowHeight-1;
  MyCamera = geCamera_Create(2.0f, &Rect);  assert (MyCamera); 

  //Create a new File System with info about level
  //IMPORTANT NOTE: substitute your desired level path & name below
  MyFileSys = geVFile_OpenNewSystem(NULL, GE_VFILE_TYPE_DOS, 
                                    "levels\\MyLevel.bsp",        //Put your level filename here
                                    NULL, GE_VFILE_OPEN_READONLY); assert (MyFileSys);

  //Create the world
  MyWorld = geWorld_Create(MyFileSys); assert(MyWorld);

  //Close our file
  geVFile_Close(MyFileSys); MyFileSys = NULL;

  //Add World to Engine
  geEngine_AddWorld(MyEngine, MyWorld);

  //Load the actor file.
  //IMPORTANT NOTE: substitute your desired actor path & name below
  MyFileSys = geVFile_OpenNewSystem(NULL, GE_VFILE_TYPE_DOS, 
                                    "actors\\dema.act",     //put name of your actor here
                                    NULL, GE_VFILE_OPEN_READONLY);  assert (MyFileSys);

  //Create a definition of the actor
  ActorDef = geActor_DefCreateFromFile (MyFileSys); assert (ActorDef);

  //Create the actual actor
  MyActor = geActor_Create(ActorDef);  assert (MyActor);

  //Add actor to the world
  geWorld_AddActor(MyWorld, MyActor, (GE_ACTOR_RENDER_ALWAYS | GE_ACTOR_COLLIDE), 0xffffffff);

  //Close our file
  geVFile_Close(MyFileSys); MyFileSys = NULL;

  //Set the animation to a default value
  geActor_ClearPose(MyActor, NULL);

  //Get bounding box
  assert (geActor_GetDynamicExtBox(MyActor, &ExtBox) == GE_TRUE);

//---------------------------------------------------------------------------------------------
//Engine is now up and running.  Now will interact with user, and move actor
//---------------------------------------------------------------------------------------------
  LARGE_INTEGER    CountsPerSecond, OldQPCCount, CurQPCCount;
  DWORD            OldCount, CurCount;
  BOOL             UseQPC;     //does hardware supports high-res counter
  tInfo            MyActorInfo;
  geBoolean        Running;
  geFloat          ForwardMagnitude;
  geVec3d          ForwardVector;
  geVec3d          tempPos;
  GE_Collision	   CollisionInfo;
  geMotion*        Motion;
  geFloat          AnimationTime = 0;
  MSG              msg;   //will hold Windows message info


  //Initialize Timing
  UseQPC = QueryPerformanceFrequency (&CountsPerSecond);  assert (UseQPC);
  QueryPerformanceCounter (&OldQPCCount);

  //Initialize some variables.
  //Note: Depending on the level load, you may need to increase the Y value of .Pos
  geVec3d_Set(&MyActorInfo.Pos, 0, 0, 0);    //set default values for actor position
  geVec3d_Set(&MyActorInfo.Angles, 0, 0, 0); // set default values for actor angles.
  Running = GE_TRUE;

  //extract the "walk" motion saved in the actor
  Motion = geActor_GetMotionByName(ActorDef, "Walk");  assert (Motion);

//---------------------------------------------------------------------------------------------
//Start the main game loop
//---------------------------------------------------------------------------------------------
  do {
    //Calculate elapsed Time
    QueryPerformanceCounter (&CurQPCCount);  //get hardward timer count from Windows
    ElapsedTime =  (float)(CurQPCCount.LowPart-OldQPCCount.LowPart);
    ElapsedTime =  (float)(ElapsedTime /CountsPerSecond.LowPart);
    OldQPCCount = CurQPCCount;

    //------------------------------------
    //Check for user keyboard input, and respond.
    //------------------------------------
    ForwardMagnitude = 0; //Default to no movement
    if (GetAsyncKeyState(VK_UP)&0x8000)		  ForwardMagnitude = 1;  //positive multiplier -> forward movement
    if (GetAsyncKeyState(VK_DOWN)&0x8000)    ForwardMagnitude = -1; //negative multiplier -> reverse movement
    if (GetAsyncKeyState(VK_LEFT)&0x8000)    {MyActorInfo.Angles.Y += 2.0f * ElapsedTime; WrapAngles (&MyActorInfo.Angles.Y);}//rotate left
    if (GetAsyncKeyState(VK_RIGHT)&0x8000)   {MyActorInfo.Angles.Y -= 2.0f * ElapsedTime; WrapAngles (&MyActorInfo.Angles.Y);}//rotate right
    if (GetAsyncKeyState('S')&0x8000)        {MyActorInfo.Angles.X += 2.0f * ElapsedTime; WrapAngles (&MyActorInfo.Angles.X);}//rotate down
    if (GetAsyncKeyState('W')&0x8000)        {MyActorInfo.Angles.X -= 2.0f * ElapsedTime; WrapAngles (&MyActorInfo.Angles.X);}//rotate up
    if (GetAsyncKeyState(VK_ESCAPE)&0x8000)  Running = GE_FALSE; //quit at end of cycle

    //------------------------------------
    //Now put our MyActorInfo into an XForm that can move our actor to the desired location/orientation
    //------------------------------------
    //  I have found it helpful to have separate XForms for posing and for movement
    //  This allows me to do whatever rotations are needed to make the actor look right, while still
    //  having another XForm that can give an "In" or forward vector that points in a logical direction
    geXForm3d_SetIdentity(&(MyActorInfo.XForm));    // clear the actor xform matrix
    geXForm3d_SetIdentity(&(MyActorInfo.PoseXForm)); // clear the actor posexform matrix

    //First we look up or down (while oriented perpendicular to the X axis)
    geXForm3d_RotateX(&MyActorInfo.XForm, MyActorInfo.Angles.X); // remove if you want to keep actor on floor
    geXForm3d_RotateX(&MyActorInfo.PoseXForm, -GE_PI/2);  //position such that always standing up straight

    //Now we can spin left or right on the Y (vertical) axis
    geXForm3d_RotateY(&MyActorInfo.XForm, MyActorInfo.Angles.Y);
    geXForm3d_RotateY(&MyActorInfo.PoseXForm, MyActorInfo.Angles.Y + GE_PI);
    //Note: I don't need or use Z rotation

    //get Forward vector, relative to XForm orientation
    geXForm3d_GetIn(&MyActorInfo.XForm, &ForwardVector);

    //use ForwardVector (scaled for time and movement magnitude) to change Pos, and store in tempPos
    //  the value 800 is an arbitrary speed factor
    geVec3d_AddScaled(&MyActorInfo.Pos, &ForwardVector, 800.0f * ElapsedTime * ForwardMagnitude, &tempPos);

    // increment the animation frame
    //Note: keeps actor in the walking animation regardless of actual movement--for simplicity
    AnimationTime += ElapsedTime;  if (AnimationTime > 1.0) AnimationTime = 0;

    // check if we hit anything by calling the GENESIS collision function.
    // it will search through the World and see if there is any collision with
    // anything in the line from Pos to tempPos using the bounding box
    // ExtBox.Min to ExtBox.Max which represents our Object minimum holding
    // box. if there was any collision, then the Collision structure will hold
    // all of the pertinent data about exactly what we hit.
    if (geWorld_Collision(MyWorld, &ExtBox.Min, &ExtBox.Max, &MyActorInfo.Pos, &tempPos,
                          GE_CONTENTS_SOLID_CLIP,GE_COLLIDE_ALL, 0xffffffff, NULL, NULL, 
                          &CollisionInfo)) {
      //Collision encountered, so do nothing.
    } else {
      MyActorInfo.Pos = tempPos;
    }

    //Now translate the XForms to the actor's position
    geXForm3d_Translate(&MyActorInfo.XForm, MyActorInfo.Pos.X, MyActorInfo.Pos.Y, MyActorInfo.Pos.Z);
    geXForm3d_Translate(&MyActorInfo.PoseXForm, MyActorInfo.Pos.X, MyActorInfo.Pos.Y, MyActorInfo.Pos.Z);

    //set the actor's pose, and position it based on Pose XForm
    geActor_SetPose(MyActor, Motion, AnimationTime, &MyActorInfo.PoseXForm);

    //------------------------------------
    //Now get camera ready to render scene
    //------------------------------------
    //Set camera to same location as player
    MyCameraInfo = MyActorInfo;  

    //Set up Camera XForm, based on Angles
    geXForm3d_SetIdentity(&MyCameraInfo.XForm); // clear out the XForm
    geXForm3d_RotateX(&MyCameraInfo.XForm, MyCameraInfo.Angles.X);
    geXForm3d_RotateY(&MyCameraInfo.XForm, MyCameraInfo.Angles.Y);
    // move the camera to the new location, and raise up to eye level
    geXForm3d_Translate(&MyCameraInfo.XForm, MyCameraInfo.Pos.X, MyCameraInfo.Pos.Y+60, MyCameraInfo.Pos.Z);

    // lets take a look! (Put the info from XForm into the Camera's internal transform)
    geCamera_SetWorldSpaceXForm(MyCamera, &MyCameraInfo.XForm);

    //------------------------------------
    //Now do actual rendering
    //------------------------------------
    // clear the back buffer and get ready to render
    geEngine_BeginFrame(MyEngine, MyCamera, GE_TRUE);

    // render the world onto the back buffer
    geEngine_RenderWorld(MyEngine, MyWorld, MyCamera, 0.0f);

    // flip the back buffer to the front
    geEngine_EndFrame(MyEngine);
    //------------------------------------

    //------------------------------------
    //Check for windows messages
    //------------------------------------
    while (PeekMessage(&msg, MyWindowHandle, 0, 0, PM_REMOVE))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    //------------------------------------
    //Hey!  We're done for this cycle.  Let's do it again!
    //------------------------------------

  } while (Running);
}
//---------------------------------------------------------
//End of Code
//---------------------------------------------------------

home