Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Wednesday, 14 March 2012

Clojure-flavoured Rendering Framework: State

My rendering framework, such as it is, sits on clojars.org and does... not very much. That should change. I can't guess how useful it'd be to anyone else, but it could certainly be a lot more friendly.

With that in mind, I started taking a critical look at a few emergent antipatterns in the very core of the framework: the OpenGL wrapper.

This example, I am happy with. Mostly.

Last time, I mentioned how the rendering functions have state very explicitly threaded through them in the form of an argument. A bunch of my wrapper functions do very simple things to mutate GL state. Some of them reset it afterwards, or wrap those that don't in a friendly way. For example, matrix stuff is done something like this:

(ogl/with-matrix
  (ogl/translate 1 0 0)
  (ogl/scale 5.0)
  (ogl/triangles
    (ogl/vertex 0 0 0)
    (ogl/vertex 1 0 0)
    (ogl/vertex 1 1 0)))


Hopefully that's fairly obvious to someone familiar with OpenGL. The with-matrix wraps a glPush/PopMatrix pair of calls around all the expresions within it. Likewise triangles calls glBegin/glEnd with the appropriate primitive type. I debated not including this ancient method of drawing stuff, but it's so handy for debugging and quick tests... anyway, the point is that even though translate and scale mutate some OpenGL state, they're intended to be used within this kind of protected block. Maybe they should all have exclaimation marks, I'm a little bit hazy of when it's polite to start exclaiming when you're not mutating your arguments.

Obviously the triangles call and all the stuff therein is side effecting like crazy, but I'm not sure how much value attaches to trying to make those calls look different. If anyone has an opinion on the aesthetics of such things, please yell.

A lot of the rendering stuff behaves similarly to with-matrix. I have equivalent calls to wrap GL attribute fiddling, wireframe rendering (mostly debug friendliness), capturing the results of rendering expressions to render targets, assigning vertex buffers, and binding shaders. Some of these are could do with a refactor to make them more consistent, but they generally behave.


But then there's this...

So that's all well and good. Then I came across the with-matrix-mode macro.

Ick.

This one looks so similar to the with-matrix example, but instead of pushing and popping from the current matrix stack to allow safe manipulation of a transformation, it changes which matrix stack is currently active. OpenGL has a few of these, but the only ones that are really relevant are your "world" transform (GL_MODELVIEW) and your view/projection transform (GL_PROJECTION). The former moves vertices around, the latter manipulates your notional camera, as it were.

The reason this makes me twitch is that this doesn't in any way revert the changes you make within it. For example, here's a little function that sets up an orthographic view in place of the conventional camera-like perspective view:

(defn orthographic
  "Loads an orthographic projection matrix to the top
of the projection matrix stack."
  ([left right bottom top near far]
     (with-matrix-mode :projection
       (GL11/glLoadIdentity)
       (GL11/glOrtho left right bottom top near far)))
  ([left right bottom top]
     (orthographic left right bottom top -1.0 1.0)))


Sure, at the end of the function the matrix mode has been restored to whatever it was at the start, but there's no attempt made to preserve the state of the projection matrix. That's rather the point, in fact - it gets murdered and replaced. Everything after this will be rendered using the orthographic projection.

I suppose this function is named in a misleading fashion. Anyone assuming the state of the GL will be unchanged after executing it will be disappointed, whereas elsewhere I'm trying to leave things as I found them. On the other hand, changing matrix mode is rather rare compared to rendering stuff, you generally do it once to set up your camera and then leave it. This kind of mutate-and-forget approach makes it relatively easy to split up the rest of your rendering into small chunks, as long as you keep things in the correct order.

Elsewhere similar compromises might sensibly be made with the excuse of efficiency; there's no sense constantly setting and then resetting a bit of state if the next expression simply sets it again, or worse, if it sets it back to the same value. All told, I probably just want to find a consistent way to signal that some functions do not behave with respect to render state, whilst the default remains that all functions clean up their toys after playing with them.

Saturday, 10 January 2009

Meanderings


The festive season never seems to be a good time to get any coding done...

With that out of the way, I'm going to press on with my crazy schemes. Over the past few days, I've been trying to recreate the skeleton of my game in Haskell using Nanocurses (this was mostly an excuse to learn Haskell, having tried and failed many, many times already).

I still think in altogether too imperative a fashion for Haskell to look on approvingly, especially when doing things with Curses involves slipping into 'do' notation all the time. Ah well, it'll be interesting to see if rendering my problem in a more pure language will help me spot any solutions.

Tuesday, 7 October 2008

Whoops, massive lack of updates

So, many deadlines at work combine with a resurgence of my WoW habit to completely destroy my hobby-code productivity...

With the next expansion looming very near (and a pre-expansion patch apparently within a week), Wowcrack is undergoing some very bizarre changes. I may splurge about the design decisions later, because they're quite interesting even if mostly specific to the online genre.

In the past month I did get a few things done. Some general hackery, a new model to replace the tentacles for general clutter, and a starter particle system (here used to show where the test lights are):



Particles are entirely GPU-side, all the simulation is done in the vertex shader operating on some static random buffers. This means they're only good for simple things, but they're pretty damn quick, entirely deterministic, and quite fun.

'Shrooms need a diffuse texture, and probably a redesign. The problem with things like these familiar mushroom shapes is that from a top-down perspective, they look a great deal like rocks. Bright colours/spots/etc would help with that, but I think a better choice would be to choose a more interesting shape for the fungi and flora. After all, they're present in a vast array of bizarre shapes in real life, so I see no reason to stick with the common toadstool shape in a fantasy cave-world.

With luck, I'll have more time for code and blogging soon. 'Til then, enjoy the minimal sparklies!

Edit: SSAO is turned off in the final composited image at the moment, because it has returned to looking a bit arse. Tricky thing to get right it seems.

Thursday, 31 July 2008

Floors melt, I boggle




Sometimes, you just can't win. Other times, the floor inexplicably melts and you're left wondering... just what is going on in this code?

This wasn't intentional, but it might look quite good over lava or acid or something. Don't slip!

Thursday, 29 May 2008

FPRL

So, after implementing some good 'ol procedural cavern geometry, here's the result as of last night. It's using the free debug camera for a first-person view. This is a really rough pass, lots of issues with the nature of the functions used etc. Plus normal mapping is currently broken, and the textures are very blargh. Lit by a yellow point light, with no falloff or shadowing, located behind the camera. Changing colour, adding shadows and falloff would hopefully make this look rather more like the view of an intrepid adventurer with a torch/lamp... in an incredibly boring, sterile and regular cavern.


The biggest problem I'm facing right now is the massive temptation to use the new shiny generation in a less restricted way. At the moment it's still using a simple grid and a heightfield, which is quite visible in the screenshot. What I increasingly want to do is switch to an entirely arbitrary environment, and then use a Wizardry 8 style combat system. Or even go entirely realtime. Blarp.

Anyway, assuming I resist the temptation to drift even further from RL-land, I need to find a nice elegant way to peel the top off of the dungeon, so we can see into this wonderland properly. Other important decisions: streaming world or discrete dungeons? Overland travel? Outside areas? Generating the dungeon mesh chunks isn't cheap, which makes me want to stick to discrete, small dungeon levels. It just doesn't feel particularly natural to do so, especially given the crazy environments now possible. Hmm.

Well, that'll do for now. Much to think about, but first priority will be sorting out normal mapping and other rendering tweaks.

Wednesday, 21 May 2008

Reinvigoration

Eventually, I got bored at fiddling half-heartedly with the planning AI and decided to do something with more immediate results.

So now the action and timing stuff are wodged into the game! I can now pilot a little green blob about a level, hacking at little orange blobs that immediately respawn, and... well, that's about it actually.

Suprisingly, this lack of progress took quite some time. I elected to multithread the game, not for performance reasons, but because it made accepting player input in a turn based situation somewhat easier. The game logic thread simply blocks until sensible input is received, leaving the rendering thread to do its thing. This may well cause plenty of problems down the line, but for now it's a simple solution that (sorta) works.

Now where to go from here... the console needs to be implemented and hooked up to the UI. Loads of actions need to be implemented. The conflict resolution system needs to be used, and behind it the associated basic system mechanics. Pathfinding is almost certainly still kaput, last I checked it liked driving the AI into walls. Random dungeons. Saving. Ah, and that's just the teeny tip of the iceberg. Gotta love making games.

Friday, 22 February 2008

Event handlers

The fascinating details of JADE's event handlers started another mental ball rolling.

The post specifically (but not exclusively) concerns 'pluggable' enhancements to equipment, which in many games manifest themselves as prefixes (flaming pig-poker, holy maguffin) and suffixes (stick of southwesternesse, crown of monobrow inducement).

Of course, the general implementation of event handling using something akin to Chain of Responsibility is a good and worthy idea. But the specifics of how to do it in a general fashion made me wonder about a few things, and wondering about things makes me note them down in order to come back later and decide there is no gold amongst the copious dross of my thoughts.

First, a rough enumeration of game events we might want to handle on a creature in the game world:
  • Taking damage
  • Being healed
  • Attacking
  • Damaging
  • Magic use
  • Receiving a status effect
  • Being stripped of a status effect
  • Inflicting status effects
  • Removing/putting on equipment
  • Consuming or using items (potions/wands/rods and equivalents)
  • Being moved or moving (physically or via teleportation, voluntary or involuntary)

Quite a few, and there'll be plenty I haven't thought of. But they're all relatively straightforward. The bit that causes me to furrow my brow is the context for these events. It seems the event handlers need to know some specifics as well as having access to the world model, so they'll all need a packet of context data more or less unique to the event class.

Another thing which causes me to scratch my head is the possibility for recursive events. A spell which reflects some portion of damage taken, for example, could go horribly wrong if two mages using it start fighting. I don't really want to make any assumptions about the depth of event propagation though, so I think this is just another pitfall to edge around rather than something to code against.


Thankfully Scala's pattern matching with case classes should fit this kind of event model quite well, and provide a natural way to bundle up context data. I'll have to try it at some point.

Wednesday, 13 February 2008

Basic asset support

Huzzah! Primitive Texture, Shader and Material objects are implemented. In the end I gave up trying to work out where the GL -> Java -> Scala chain was breaking down with glGetInfoLogARB, and just started checking the shaders by hand with the GLSL Syntax Validator (which, by the way, is rudimentary to say the least).

That done, it was but a short head/wall reciprocatory interlude before DevIL was playing nice too, and I could start cobbling together some test shaders. Of course, lacking anything except Paint.NET (a very nice but somewhat lightweight image editor) I didn't spend long on the textures. Behold the mighty effect of a Julia set rendering combined with Clouds for a normal map!


There's also a bit of hackery going on with the worldspace position and frac (or fract in GLSL parlance, because that extra 't' sure makes a difference to legibility! Bastards.) to draw a rudimentary grid too. My thinking is that a shiny'd up version of this will be on a button - maybe tab - to help with tactical stuff on natural terrain where it's hard to gauge distance by eye. The nasty big black grid is just an error in the normal map. Yes, I'm so lazy I can't even be bothered to make any of it tile nicely at this point. Or give them mipmaps. Aliasing is lovely, don't pretend you don't want it.

Also great news, the Java wrapper for SQLite plays nicely with Scala. I've been wanting to do this for a while and not done it because it'll take some planning, but having a real database at the heart of this game should add a huge amount to extensibility. The ultimate aim is to have in-game creation of new enemies, items and spells, at least to a limited degree, by allowing editing the databases through console commands. Coupled with LuaJava for scripting, this should be fairly powerful, but will need a vast amount of work to make really nice methinks.

Even just creating variants and advanced forms of existing game objects could be a massive bonus though. After all, roguelikes and CRPGs in general traditionally reuse enemies painted a slightly different hue, often to denote some elemental or behavioural trait. The idea of wandering down a dungeon, finding a level that looks a bit sparse and creating a new flavour of orc with red skin and tons more hitpoints than his brethren is just... good. I look forward to seeing if I can pull it off.

Sunday, 10 February 2008

Natural terrain

More OpenGL wrestling occurred, but eventually I bent it to my will and managed to get VBOs working.

The dungeon now has a floor and ceiling that varies in height, and a very rough first stab at a continuous mesh:


Now, there are many things wrong with the implementation so far, not least of which is the lack of textures and shaders. Those will require me to gird my loins and head back into the terrifying world of the GL. No, perhaps more fundamental is the way solid areas (natural rock in this case) are represented. Right now I did a quick hack and pulled the terrain up to mimic the effect, but this causes nasty artefacts thanks to the stretching of polys and is far too restrictive. A different approach would be needed for crafted walls.

I'm thinking a better approach will be to create a separate wall mesh by extruding the lines defining a change from solid to traversable terrain. There are some big potential problems with this, and another method is still required for constructed rather than natural areas, but I think on the whole it's a win. It also reduces the amount of geometry to be recalculated when creatures dig or destroy walls.

So the basic dungeon components are looking like:
Floor/ceiling - heightfield, possibly with discrete mesh tiles (flagstones)
Rock and ruined walls - extruded continuous mesh with procedural variation
Worked walls in good order, unique features - discrete meshes, possibly welded together.

A new map format will be needed soon. The .txt map generator is beginning to look a bit under-featured, especially for representing material differences. It may still be the first step in map creation though, as I like how easy it is to check the output.

The heightfield meshes still need to represent material variation (rock, sand, vegetation, pebbles) and there may be additional mesh layers (water, lava, ground cover). So much to do, and as ever so little time...

As an aside, my rough thoughts on combat with height variation:
Melee - if the attacker is half a tile-width or greater above the defender, attacker is at +1*. If half a tile lower, attacker is at -1. Height differences equal or greater than 1 tile prevent both standard movement and melee attacks.
Movement - movement uphill takes more energy than moving on a flat, and movement down less. I think this'll cap at around double/half cost for the max gradient. Maximum gradient for movement without a 'climb' action is 45 degrees. Non-climb movement down a slope >45 degrees counts as falling, and will cause damage roughly proportional to the height difference squared. There will be rules for falling/climbing on inclines that are greater than one tile wide.
Ranged - projectiles deal increased damage and have increased range when fired from an elevated position
LoS - high tiles may block LoS, depending on height relative to player and 'interesting objects' (monsters) in potentially hidden tiles.

* Where +1 is a small attack and damage bonus. Possibly +1d4 to both - it depends on the exact mechanics for combat and stuff, which are currently not defined...