Wednesday, August 31, 2011

I have to be the worst blogger ever.. (also, a video!)

Despite the usual sparsity of blog updates I've been working a lot on the engine and the editor over the course of this summer and I'm proud to say that I've made quite some progress! Considering that I haven't been writing anything in months and that noone probably remembers what this is all about anyway, I feel like I should give a quick recap.

My motivation (or justification)
Some of you may remember RealityFactory, a gameshell based on the then popular open source game engine Genesis3D. The idea behind RealityFactory was (and apparently still is - there still seem to be some people around from back then!) to allow non-programmers to create computer games, merely relying on a set of level and game editors and some scripting. Frustrated by my own puny attempts at writing games using Genesis3D by essentially hacking the included (and horribly written) game shell I became a huge fan of the RealityFactory project and stayed active for several years. That was around 2003 and 2005. Time went by and eventually, people started asking for a replacement of the outdated Genesis3D engine. I raised my hand and teamed up with an American guy who at some point lost interest but the idea of creating a piece of software that would give people the chance to realize their games and learn as much I had with RealityFactory kept me going.

Looking for a suitable graphics engine to base my RF2 candidate on, I stumbled upon NeoEngine. The project was ultimately abandoned by its founder; while trying (and for the most part failing) to add new features myself I realized that graphics engine programming didn't seem to be all that hard after all, and after I had taken a look at some other open source engines and decided I didn't like any of them (basically because I hadn't written them - of course most of these were and still are great pieces of software) I decided to write my own graphics engine which would later develop into a full-blown game engine.

That, of course, never happened. I got so tangled up in improving graphics that I forgot about my original intention of creating a game engine. The result was a decent but utterly slow graphics engine with support for some basic physics. And, of course, I learned a lot about how NOT to plan a project. You can still find screenshots and videos of this project in previous blog posts. So in the summer of 2009, I started from scratch. I built up a stable foundation first, utilizing all the knowledge I gained in the process of building the previous engine and considered such (basic) concepts like interfaces for sub-systems like graphics, audio, physics and scripting and the integration of more third party libraries, first and foremost boost. Since then, I've been working on and off. This summer has been very productive - I've been on semester break, my part-time job is pretty relaxed, the girlfriend visting her parents, and so I've been working eight to ten hours a day for the last few weeks.

Wunderwerk Engine ("the engine")
The engine's core consists of four sub-systems: graphics, sound, physics and scripting. All but one (physics) are functioning and more or less complete. The graphics engine currently uses OpenGL 3.3 in the core profile. A switch to OpenGL 4.x would exclude many and gain little, so I'll wait. Right now, only a very basic rendering pipeline is implemented, allowing for simple per-pixel shading. I'm planning to implement and extend the old engine's deferred shading pipeline and post-processing framework soon. Have a look at older posts for some screenshots and videos. The sound engine uses OpenAL and is more or less finished. It features playback and mixing of positioned 3D sound and music. Not much to say here, really. The old engine's physics engine was powered by Bullet Dynamics and will be incorporated as soon as the octree implementation is finished. Scripting is realized using the Lua scripting language, which I've grown quite fond of. The entity system exposes most of its functionality through a simple, C-style scripting API.

Here is an example of a (very, very) simple AI script; a zombie approaches and reaches for the player depending on their distance to each other (read bottom to top):

 function idle( deltaTime )  
      -- blend idle animation  
      stopCycle( "Zombie", "Skeleton", "Walk", 0.5 )  
      playCycle( "Zombie", "Skeleton", "Idle", 1.0, 0.5 )  
 end  
   
 function attack( deltaTime )  
      speed = 0.6  
      rotSpeed = 4.0  
   
      -- fade idle animation  
      stopCycle( "Zombie", "Skeleton", "Idle", 0.5 )  
        
      -- query zombie's health  
      health = getAttribute("Zombie", "Health")  
        
      if health > 50 then  
           -- blend walk animation  
           stopCycle( "Zombie", "Skeleton", "Limp", 0.5 )  
           playCycle( "Zombie", "Skeleton", "Walk", 1.0, 0.5 )  
      else  
           speed = 0.25  
           rotSpeed = 1.5  
   
           -- blend limp animation  
           stopCycle( "Zombie", "Skeleton", "Walk", 0.5 )  
           playCycle( "Zombie", "Skeleton", "Limp", 1.0, 0.5 )  
      end  
        
      -- get position vectors  
      eyePos = getTranslation("Zombie")  
      targetPos = getTranslation("Player")  
      targetPos = vec3( targetPos.x, eyePos.y, targetPos.z )  
        
      -- compute look at matrix  
      lookAtMatrix = lookAt( eyePos, targetPos, vec3(0, 1, 0) )  
        
      eyeRot = getRotation( "Zombie")  
      targetRot = inverse(quat(lookAtMatrix))  
      rotation = shortMix( eyeRot, targetRot, rotSpeed*deltaTime )  
        
      -- make zombie look at player  
      setRotation( "Zombie", rotation );  
        
      -- move towards player  
      translate( "Zombie", vec3(0, 0, -speed*deltaTime) )  
 end  
   
 function slam( deltaTime )  
      -- is slam animation already playing?  
      if isActionPlaying("Zombie", "Skeleton", "Slam") == false then  
           -- stop animation cycles  
           stopCycle( "Zombie", "Skeleton", "Walk", 0.25 )  
           stopCycle( "Zombie", "Skeleton", "Idle", 0.25 )  
             
           -- blend slam animation  
           playAction( "Zombie", "Skeleton", "Slam", 0.15, 0.0 )  
      end  
 end  
   
 function update( deltaTime )  
      -- get position vectors  
      pos = getTranslation( "Zombie" )  
      target = getTranslation( "Player" )  
        
      -- calculate distance from player  
      distanceFromPlayer = distance( pos, target )  
        
      -- slam if very close  
      if distanceFromPlayer < 1 then  
           slam( deltaTime )  
      -- walk towards if close  
      elseif distanceFromPlayer < 5 then  
           attack( deltaTime )  
      else  
      -- ignore if far  
           idle( deltaTime )  
      end  
 end  

Resource managment is finished and the entity system is basically complete. Static and animated models can be exported from Blender, which is actively developed and most importantly, free. Textures have to be provided in .dds format; there's exporters for both Gimp and Photoshop. The sound engine expects files stored in the OggVorbis format, which provides quality similar to MP3 without the competitor's license restrictions.

The entity system uses a component-based approach in place of the classical inheritance-based one, which means that there is e.g. an entity class Model which derives from a base class RenderEntity which in turn derives from an abstract base class Entity. Instead, an entity is merely a container for an arbitrary number of entity components such as models, sounds, cameras, decals etc. All these components have in common are a spatial or logical connection.  There's numerous threads on gamedev.net dealing with the pros and cons to both approaches, in case you're interested. What was important to me is that the entity components themselves and the camera rendering them are ignorant of the entities these components have been attached to. Instead, whenever an entity component is added to an entity, it is added to a pool of like components and merely keeps a reference to the transform part (i.e. translation, rotation, scaling) of its parent entity. During rendering, a camera then fetches relevant components from these pools and renders them accordingly.

Composing e.g. a player entity which contains an animated model, the corresponding skeleton and a camera could look like this:  

 shared_ptr<Entity> setupPlayer( shared_ptr<ResourceManager> resourceManager )  
 {       
      // load resources  
      auto model = resourceManager->loadResourceAs<Model>( "ak-47.model" );  
      auto skeleton = resourceManager->loadResourceAs<Skeleton>( "ak-47.skeleton" );  

      // setup camera  
      auto camera = shared_ptr<Camera>( new Camera(60.0f, vec2(1.0f, 1000.0f)) );  
   
      // setup mesh controller  
      auto modelController = shared_ptr<ModelController>( new ModelController(model) );  
   
      // setup skeleton controller  
      auto skeletonController = shared_ptr<SkeletonController>( new SkeletonController(skeleton) );  
   
      // add skeleton as modifier  
      modelController->addModifier( L"SkeletonModifier", skeletonController );  
   
      // setup new entity  
      auto entity = shared_ptr<Entity>( new Entity() );  
   
      // add mesh controller to entity  
      entity->addComponent( L"WeaponMesh", modelController );  
   
      // add skeleton controller to entity  
      entity->addComponent( L"Skeleton", skeletonController );  
   
      // add camera to entity  
      entity->addComponent( L"Camera", shared_ptr<CameraController>(new CameraController(camera)) );  
   
      return entity;  
 }  

This is tedious and we don't want users to have to deal with writing code anyway, so that's where the editor comes in.

Weltwunder Editor ("the editor")
The editor's task will be to populate the game world with static geometry (the "level") and entities and to attach the appropiate scripts to them. The game world you create is the starting point of the simulation when the game is started. The player's interaction with the world and its entities is then controlled by scripts. Scripts can either be executed once each frame or started from other scripts, e.g. when the player enters a specific area or presses a button.
The editor uses the wxWidgets framework and would so - theoretically - run on multiple platforms. Right now, I'm developing on and for Windows only, though. It also has, just like the engine, full support for Unicode and is currently available in English, German and French.

The following short video shows how to create an entity representing an animated mesh model. It could be extended by adding headlights, muzzle flashes, sound effects, etc. Using the entity's name, all components can then be controlled in scripts.


Over the past week, I was racking my brains over the degree of asset customization that should be available to the user from within the editor. Usually, assets exported from Blender are perfectly fine and ready to use, but the engine supports some advanced material settings such as several texture layers each with different blend modes, texture coordinate generators, etc. which cannot be customized from within Blender. The problem is - how would I save such customizations? Should I overwrite the original files? What if the user re-imports his assets? In the end I decided to either write a small Blender plug-in or a stand-alone material editor.

The game shell
The game shell loads a world created by the editor and handles script execution to simulate a game. It works.

What lies ahead?
Right now, my focus is on the editor, even though I'm tempted to finally add some pretty graphics. I hope to finish entity management within the next four weeks, and then start on static, i.e. "level" geometry

I'm writing all this because everybody keeps asking me what the hell I'm doing all day and whether I'm still working on "that game of yours that you never finish" and also because I'm tired of always working for myself when I should share my experiences with others. So if you've got any questions, comments or need help with your own little renderer slash game engine, leave a comment or write me a message. Also, I'll try to write some lines about my progress on the engine at least once a week. But then again, that's what I always say.

2 comments:

  1. Pretty nice stuff :)

    I am also working on my 3D engine and editor (using wxWidgets). If you like it would be pretty cool to share experiences.

    Website is http://www.vapor3d.org/.

    ReplyDelete
  2. Sounds good! I've added you on GTalk.

    ReplyDelete