Wednesday, December 24, 2008

Initial TideTurner Gameplay Concept

/trunk now contains a very preliminary concept demo of some ideas for TideTurner, here's a video:



I'll have to figure out how to improve the clarity when I prepare a video for the 0.3 release. But here is what you should be seeing:
  • The player controls a character that can cast spells. Two spells are shown: Creating a skeleton minion, who fights for the player, and creating a force barrier to keep enemies away.
  • Casting spells takes energy, which regenerates, so the player needs to decide which spells to cast at what time.
  • Enemies arrive from two entrances to fight the player and his minions (the enemy skeletons are in red).
  • A successful strategy should probably involve blocking one entrance while summoning enough minions to overcome those flooding through the other one.
  • The force barriers - shown as semitransparent yellow - vanish eventually. Not preparing for that can lead to your demise, as shown in the video ;)

You can playtest this yourself right now, by compiling and running /trunk (use bzr lp:intensityengine to check it out). Revision 209 is what is shown in the video, by the time you read this a later revision might be current (which might be different or even broken; revert to rev. 209 if so). Note that you might need to start with a fresh server for things to work correctly, depending on what you currently have in /storage. This can be done simply by deleting the /storage directory under the installation directory, but note that this deletes all of the server's data, so if you have any maps of your own, back them up first.

Sunday, December 21, 2008

Skeletons in /trunk

Work on TideTurner led to various bugfixes and small feature improvements in the engine during the past week (for example, more uniform handling of positioning for both static and dynamic entities). It's always the case that actually using a product leads you to find all sorts of small things that you don't think of during development...

/trunk should now contain a usable environment for testing of TideTurner: I imported all the media files, etc. - I don't like the idea of large binary files in a version control system, but really there is no alternative. I'll also try to set up a hosted server so that multiple people can playtest at once (I need to recompile the server for the appropriate distro). Finally, I set up a section in the forums for discussion of TideTurner development. Meanwhile, here is a random screenshot:


The main character model is by Tiziana aka TiZeta and the skeleton by Adam Ward aka Gixter. Both are models that can be used freely. I intend to replace them eventually, once I find an animator to work with on this project, but for now they should be good for playtesting.

The initial game idea is for the main character to have a few spells, one to summon a skeleton, another to create barriers (you can see one in the screenshot, it's the semi-transparent yellow area), and maybe a fireball for combat. The character will then fight another group of skeletons, perhaps led by another player, with the barriers being of stategic importance, to prevent your opponents from surrounding you, giving you time to regenerate health (the barriers will vanish after a certain time period, or if they have been damaged enough), and so forth. So far in trunk you can create barriers with a click and skeletons with a rightclick, and clicking on a skeleton makes it walk to you - still in a very early stage here.

Here is the code in charge of creating barriers:


create_barrier = StateArrayFloat()

BUTTONS_TO_SPELLS = { BUTTON_LEFT : "create_barrier", BUTTON_RIGHT : "create_minion" }

def click_action(self, button, x, y, z):
self.clear_actions()
self.queue_action( SpellcastAction([x,y,z], Commander.BUTTONS_TO_SPELLS[button]) )

def can_modify_create_barrier(self, value, actor_unique_id):
self.barrier = create_logic_entity(Mapmodel)
self.barrier.model_name = "forcebarrier"
self.barrier.position = value
self.barrier.yaw = yaw_to(value, self.position)

return False # No need to change this value on the clients


It might not be entirely self-explanatory, as you need to have an idea of what StateVariables are and how can_modify_X functions work, but I think it does show how only a little Python code is necessary to do stuff like this.

Sunday, December 14, 2008

Two minor updates

  1. Today I thought about how map creation could be made more convenient. In particular, I was unhappy with the fact that cube geometry is saved in .ogz files while entities are stored in the database, and so far in order to copy that entity data I've basically been doing SQLite dumps. Now, it does make sense to separate the two kinds of data (cube geometry is static, while entities are not, in particular, you might want to take 'snapshots' of entity activity for various reasons), but being forced to use database exporting is not good.

    So it seemed a reasonable idea to write dump_entities() / read_entities() functions, which would save all current entity data to a file and read such a file, respectively, in a portable manner (so it doesn't matter what database you're using, if at all).

    At first glance, it wasn't apparent how hard it would be to write such functions. But after a bit of thought, it became clear that my hard work from earlier in the project's history would pay off. For one thing, in the Intensity Engine we do entity lifecycle management is in a unified, high-level manner. For example, there is a single create_logic_entity() function, which encapsulates differences between Sauerbraten's dynents and extent data types. Another useful architectural feature is that we store all entity information in just one place: its StateData (well, to be accurate, two: there is also the Class field). Finally, the Intensity Engine has a clean separation of database operations from everything else, that is, creating entities and storing them in the database are conceptually and practically separate.

    As a result, dump_entities() and read_entities() take only about 10 lines of Python code each :) It's definitely a good sign when adding features is as easy as that.

  2. A second issue I'd like to mention is that following a suggestion from SephoD on our forums, I looked into generating scenery from a heightmap. This differs from the procedural generation approach I did previously in that it uses a 2D heightmap instead of 3D full/empty information.

    This is a little trickier than 3D scenery as for heightmaps we want a finer resolution, which necessitates a lot of cube corner pushing, and there are issues with cube alignment boundaries (cubes can only be made at coordinates which are powers of 2). So far I have some partial results:



    (Click for a larger version.)

    Admittably this isn't terribly clear, but it shows a large 'valley'. Note that particularly near the center of the valley there are some artifacts, these are alignment issues that should be smoothed out in the future. They actually give an interesting effect, but that is completely non-intentional ;) In any case, the code is already useful for mappers that don't mind doing a little manual smoothing after the heightmap is generated.

Friday, December 12, 2008

Planning a Game

Version 0.2 of the Intensity Engine has been released. Highlights include procedural generation support, faster NPC movement and CEGUI 0.6.2b (for the full announcement see our forum). This incremental release includes most of the basic features I want to have in the engine at this point. I'll be constantly adding more, but major features will wait, as I am now going to focus on creating a game with the engine.

Towards that goal, I'm looking for people to team up with. Specifically, I'm looking for two people:
  • a 3D artist/modeler
  • a (C++) coder
Formal experience is unnecessary, as long as you know enough to get started and are motivated to learn and improve.

The codename for the game is TideTurner (the actual name will be determined later). The general game concept is a third-person action game, with the players leading NPC minions in battle. Aside from that, the specifics are yet to be determined. Playtesting will decide which ideas end up in the game and which don't.

If you might be interested in collaborating on this game, let me know, on IRC (#intensityengine on FreeNode), or by email at kripken -at- intensityengine dot com.
Note that the engine is open source, but that does not preclude the game from being commercial. In particular, we can try in-game advertising to make money. Profits will be shared among all team members, of course.

Friday, December 5, 2008

Procedural Generation

Creating a landscape can be a time-consuming task. Because of that, it is interesting to consider procedural generation, where you let a script do the work for you. Classic examples of procedurally-generated terrain are fractals; they are a simple but effective way to create realistic-looking landscapes, and also things like trees.

As the Intensity Engine is already integrated with Python, it was a natural idea to script the generation of terrain using that language. So, here is an example of a giant spiral generated by Python:


That's what it looks like from the ground, and this is what it looks like from above:



This is generated by a short Python script, basically just to implement the equation for the spiral, which is something like x = r(t)*sin(t), y = r(t)*cos(t), z = h(t), where r(t) is a radius, r(t) = a*t+b, and h(t) is the height, h(t) = c*t. t goes from 0 to 1. It's pretty much that simple.

The real work was in processing the output of the Python script in C++. This is done by a general algorithm (not specific to the spiral - it can be used for anything) that generates appropriate Sauerbraten cube geometry from the raw data, and then applies some smoothing to the result to make it look more natural. The code appears in editing_system.cpp if you are interested.

Using that code, one can now create a lot of different types of scenery automatically. I have so far made some examples, of a sphere (which isn't perfectly round - it needs more smoothing) and staircases. These are the sort of things that can be tedious to do by hand, that Sauerbraten mappers currently spend a lot of time on. In fact it is possible (minor tweaking might be needed though) to perform procedural generation in the Intensity Engine and then import that map into Sauerbraten, which would open up a lot of interesting possibilities for Sauerbraten mapping (contact me if you're interested and I'll help out). The code might also be ported to Sauerbraten, but that would not be trivial as it relies on some Intensity Engine specific features (Python integration and loading of maps on the server).

A video of the spiral, and it's creation process, can be found on the screenshots and videos page.

Monday, December 1, 2008

Ogre - Deferred for Now



I have decided to temporarily suspend Ogre development at this point. The screenshot above shows the state I managed to get it to thus far. You can see there a floating white object, which casts a dynamic shadow rendered with Ogre. Note that the dynamic shadow and the static shadows from Sauerbraten are combined in a reasonable manner, in particular where they overlap they don't create a yet darker area, which is the result you get with the simplest shadowing approaches.

To prevent that problem, I added an alpha channel to the static shadows, which is used to combine them with the dynamic shadows (but not with the diffuse textures). In effect, dynamic shadows are suppressed in areas of static shadow. This has a few drawbacks - in particular, it is hard to get the dynamic shadows to be as dark as the static ones, lightmap textures are 33% larger, and static lighting is not as vivid as it might be - but a better solution would require writing custom shaders.

But shaders would be required anyhow as depth shadow mapping in Ogre requires it (I was hoping this would be easier, but it isn't), and without depth shadow mapping you have various problems with shadows (casting in the direction of the light, not just away, etc.). I don't intend to make a serious effort at this point in time to design appropriate shaders and so forth. So Ogre will wait for now, until someone decides to work on it.

However, a great deal of the required work has already been done: Set up Ogre correctly; feed cube geometry into Ogre; synchronize entities to Ogre (lights, etc. - Ogre's dynamic shadows must have the same orientation as Sauerbraten's static shadows!); utilize Sauerbraten's lightmaps; and so forth. All that is really left is to write shaders for shadowing, and some interfacing for odd bits and pieces (particle effects, etc.). So, when I or someone else returns to the matter of Ogre rendering, it shouldn't be too hard to finish the job. It's just that there are more pressing things at the moment (in particular a 0.2 release).

Saturday, November 29, 2008

Entity List, and Lightmaps in Ogre

I'm going to make an effort to update this blog at least once a week, as I believe constant updates are good for showing people what's going on with the project, as a 'history' of the course of development, and also as a way for me to think out loud.

Ok, steady progress on all fronts. A few more bugs fixed/features added, including a much needed entity list (well, tree really):


This is useful because in a large map you can forget where entities are. Clicking on an entity in the list focus you on it, letting you edit its details without finding it in the world. It also faces you in its direction, so if you want to get to it, you just need to press 'forward' until you are as close as you want.

The tree uses CEGUI's Tree class, which required a few workarounds, in particular a patch to the Lua scripting module to support TreeEventArgs (submitted to CEGUI). Taking into account a previous patch to CEGUI, this means we really should start bundling CEGUI, at least until upstream releases a version containing the patches. If anyone can volunteer to help with this on Windows that would be greatly appreciated.

Also, there is progress on the experimental Ogre branch. We now have both the normal ('diffuse') textures combined with the lightmaps, giving us the correct appearance for scenery, pretty much how it looks in Sauerbraten in fact:


The current method for doing this is to generate three sets of texture coordinates, for each of the 6 orientations of a cube (opposing orientations are combined), and storing them in the buffer object. We then blend the lightmap and diffuse textures using appropriate Ogre texture units. This should probably be done using a custom shader, as eihrul explained Sauerbraten does, as the diffuse coordinates are trivial to calculate in 'realtime', in a shader.

In fact writing a custom shader might be unavoidable. As you can see in the screenshot, we have a shadow that looks different, cast by the floating white object in the center of the screen. This is a dynamic shadow generated by Ogre. While it has the same orientation as the lightmap shadows, it combines poorly with them, as the two shadow types combine (that is, by walking into a scenery shadow, you cast a shadow inside that area). The solution is to combine the shadows in a non-linear manner; a 'minimum' operation seems right, so that if either type of shadow is present, or both, you get the same shadow result (of course it isn't that simple, but that's the general idea, I think). It appears that this requires a custom shader, as standard Ogre texture units don't support this sort of thing. This is somewhat regrettable as it means either using Cg or writing separate GLSL and HLSL shaders, both approaches of which have their drawbacks. I'll do some testing to see which is better.

Wednesday, November 26, 2008

Ogre Progress

So far all high-priority bugs have been fixed for the 0.2 release. That leaves a few medium-priority bugs, and the creation of a more fun example game, which I hope to do in a week or so.

Alongside that, I find myself spending an equal amount of time on Ogre rendering, as it's more challenging. The latest issue I've been working on is shadows. One nice thing about Ogre is that, as a general-purpose 3D engine, it has various shadow techniques that you can apply with a single command. Here is an example of stencil shadows:


However, this approach would almost certainly lead to significant slowdowns, for more complex maps. So a better idea would be to use the already-existing Sauerbraten method of baked shadows, which are pre-calculated using ray-tracing techniques. After some helpful explanations on IRC by the one and only eihrul about Sauerbraten's rendering pipeline (thanks again!), I managed to get the brightness part of the Sauerbraten lightmaps to render in Ogre:


The next stage would be to set up materials that combine the underlying ('diffuse') textures with the lightmaps, giving the correct final appearance. This is not trivial as it requires processing two sets of texture coordinates at the same time, one for the diffuse texture and one for the lightmap (which should multiply the diffuse texture, thus either darkening or lightening it), but hopefully it won't be too hard to do.

The next tricky bit would be to combine these shadows with dynamic shadows for moving objects. They all need to look well together, even though they are generated by entirely different procedures. So the angles, intensities, etc. must match.

Sunday, November 23, 2008

Ogre Rendering?

Following the release of 0.1, I've spent my time doing a few fun things besides straightforward coding. One of them is that I've been looking into moving to rendering using Ogre. The main downside to this is that Ogre wouldn't be as optimized for the Sauerbraten geometry and so forth, so there would be a significant performance hit. It would also in all likelihood lead to larger executables and RAM usages.

Still, the benefits are interesting enough for me to spend a few days playing around with the code:
  • The main benefit is that Ogre is developer-friendly and extensible; adding new effects and so forth in that framework is much easier than in the Sauerbraten one.
  • A further benefit is that Ogre abstracts the underlying rendering API (OpenGL, DirectX, etc.), and includes plugins for both OpenGL and DirectX; this is of interest to me mainly because DirectX drivers are at times of higher quality on Windows. (More effort put into them by Microsoft, perhaps?) It also opens up far more feasible opportunities to port to other rendering methods (OpenGL ES for mobile devices, and so forth) - this would be quite cumbersome with Sauerbraten itself, as OpenGL code is interleaved with application code all around.
  • A final benefit, but of lower importance, is that feature-wise Ogre has several attractive capabilities, like various shadowing algorithms, multiple poses for meshes, and also that Ogre has a larger developer community around it.

In the end, it'll come down to how complicated it is to do, and how much of a performance hit it incurs. So, I set out to estimate both things. So far I have a very initial demonstration of rendering cube geometry using Ogre instead of the Sauerbraten rendering system - basically, I take the output (vertexes, triangles, etc.) of the Sauerbraten pipeline and convert it into an Ogre-appropriate form. Here is a screenshot:


It's not much to look at, in fact it is downright offensive to the eyes :) But it shows this can be done.

This was not too complicated, but not easy either. However, I am of the opinion that further integration with Ogre - lighting, models, particle effects, etc. - will be somewhat easier with this framework in place, and given that I will be using almost 100% Ogre for them (whereas here I still utilize almost all of the world geometry processing code from Sauer).

Performance-wise, this wasn't much of a test - just a simple map with a few thousand polygons. Still, it didn't reveal any immediate performance problems, so it's good enough to continue forward with.

My thinking at this point is that I'll continue to hack on the branch dedicated to Ogre, while continuing forward with the main branch alongside that. The only thing I might do differently is take care not to do any work in the main branch that might conflict with merging the Ogre branch later on (so, e.g., I won't work on a nice interface for Sauerbraten particle effects, since I might end up using the Ogre system for that anyhow).

My current goal is for a 0.2 release in about a month, which would include bugfixes and several minor but important features. I'll probably make a decision regarding Ogre around that time, and assuming I do decide to go with Ogre, then the 0.3 release might be dedicated to that purpose.

Friday, November 14, 2008

Introduction

This blog will contain news regarding the Intensity Engine, an open source platform for immersive, distributed 3D worlds.

The Intensity Engine is the product of 6 months of intensive coding, starting from a solid base of proven open source projects (Sauerbraten, CEGUI, Python, Storm, and others). We are now at the point of releasing version 0.1 and opening a public website, so people can start evaluating the project. As a 0.1 release, there are plenty of rough edges, of course, but hopefully the overall direction of the project should be fairly clear.

For more details, go the the main website, where you can see screenshots and videos, download the source or pre-compiled binaries, read tutorials and sample code, etc.

Here is a demonstration of the Intensity Engine in action:



3D models of squirrel and ram: (C) Blender Foundation; part of the the Yo Frankie! game.
Music: (C) Zero Project, "Gothic"