Showing posts with label gaming. Show all posts
Showing posts with label gaming. Show all posts

Thursday, 4 April 2013

There is a light and it never goes out

I've not really done much on Offender over the last couple of months, instead I've been playing a lot - Deus Ex: Human Revolution, Crysis, Mass Effect 3 and a bit of Fallout: New Vegas. Kind of interesting that the more time I spend working with OpenGL the more I look at these games and think "how would I do that?"

I've started putting lighting into my terrain. For the uninitiated, 3D APIs generally use three types of lighting. Ambient lighting is applied evenly to all surfaces, modelling light that scatters and bounces all over the scene. Diffuse lighting takes a direct path from the source, but scatters when it hits a surface, so surfaces facing the source appear brighter no matter where the viewer is. Finally specular lighting reflects off shiny surfaces, creating highlights at points which reflect light back to the viewer. Fixed-function pipelines typically provided all of these, these days they all need to be implemented with shaders.

For my terrain I'm just using ambient and diffuse lighting. Specular highlights are more complex than the other two, and grass and rocks generally aren't all that shiny so it's not really necessary. Water is shiny, but I was thinking of doing that as a separate entity to the terrain with its own shader. With the default per-vertex Gouraud shading specular highlights can look a bit iffy - you really need Phong shading, but that's per-pixel so it inflicts a lot of computation on the fragment shader. Having said this, bump-mapping might be worth doing and that's also per-pixel - but that's one for later methinks.

Also most of the sample shaders I've seen convert everything into eye coordinates (i.e. relative to the viewer), whereas I've just left everything in world coordinates (i.e. relative to the world origin). This saves multiplying everything by the view matrix, and as ambient and diffuse lighting are viewer-independent it shouldn't make any difference. With the only light source being the sun at a fixed infinity, I've been able to get away with really simple shaders

However the lighting has emphasised a problem with my terrain generation, as there are prominent "ripples" across the surfaces. That'd probably look great on sand dunes, not so good on grass and rocks. This is almost certainly an effect of using an LCG for random number generation, though I've not done the maths to try and explain it properly - something to do with serial correlation I guess?

The reason I'm using an LCG is that it's fast, and I'm reluctant to move to a better algorithm if it's going to be prohibitively slow. I experimented with a CRC32 to get rid of those ripples, it looked a bit better but symmetrical - again there's probably a good mathematical reason for this. However combining an LCG and CRC produced decent results.

LCG-based terrain. See those ripples.CRC-based terrain. Strangely
symmetrical (and a bit ripply)
LCG/CRC combo. Much better.
To see if moving away from an LCG made terrain generation noticeably slower I added some crude timing info to my debug build. The total time for calculating the vertices alone went from about 5ms per tile (pictured terrain is 3x3 tiles) with just an LCG to around 50ms with the LCG/CRC hybrid, a 10x increase! That kinda vindicates my decision to go with an LCG in the first place. Switching to the release build it took about 5ms for LCG/CRC vertixes. Timing the rest of the initialisation for comparison, the only other significant block was the bit which copies data to OpenGL buffers at around 8ms. So very approximately the vertex generation with LCG/CRC is a third of the time taken to generate the tile, and the total time for the tile is around 16ms - a frame at 60Hz. I can live with that.

Incidentally, I made a few discoveries while doing this. Extracting textures from files takes a long time - ~220ms for 3 textures, about the same in release and debug. I was re-loading the same textures every time I generated a tile when I should be sharing the textures, so fairly obvious room for improvement there. I also found that if I invoked the release build from outside Visual Studio the textures didn't load. Some path issue I guess? I really must learn to put in helpful error messages rather than flippant remarks or swear words.

My next step was going to be expanding the area by generating terrain on-the-fly. Given that it's taking around a frame to generate a tile and the CPU has plenty of other things to be doing, I'd need split the generation across multiple frames in any spare time remaining before the end of frame. Unfortunately OpenGL's syncing options are fairly limited, so it'd be best done in a separate thread... and I really don't want to go multi-threaded yet, because threads are evil. Honestly, I'm still not great at C++ and having enough trouble debugging strange behaviour without threads introducing a bunch of concurrency issues and non-deterministic behaviour. So I'm going to park this idea until version 2.0. For now I'll either stick with a load of terrain generated at initialisation, or create some height maps.

Speaking of strange behaviour, I noticed that at certain points the player object would start to vibrate violently on screen. I realised it was actually the camera vibrating, it's just that the player is the only thing close to the camera, and the cause was numerical instability in my matrix inverse. That was solved by reordering the rows in the matrix, such that the element the algorithm pivots on is the one with the largest absolute value. Ultimately it was a simple fix, but it was an interesting problem as it illustrated the practical implications of a mathematical phenomenon. I chose to procrastinate on putting this pivoting in, so it just goes to show that taking shortcuts comes back to bite you in the long run so you're better off doing things properly in the first place.

Next up, time to divert my attention back to the player object as it seems faintly ridiculous to have vast swathes of detailed, accurately lit terrain with nothing but a matte purple wedge cruising over it.

Wednesday, 15 August 2012

We are dancing mechanic

Just as I've started thinking about making a board for a Raspberry Pi based robot, someone goes and makes the mother of all robotics boards - the Gertboard. Curse you Gert Van Loo! Actually it's a very nice piece of kit, but it's bigger and more expensive than the Raspberry Pi itself, and I don't think it's really what I want. It's given me a few ideas though.

Some circuitboards, yesterday
The most powerful component on the Gertboard is the Atmel AVR microcontroller, which is the same chip that's at the heart of the Arduino, but it seems like overkill for my purposes. Do I really need a microcontroller on the robot when I've got a Raspberry Pi sitting next to it? It's cheap, and looks like it's got on-chip D/A converters so I could probably use it to drive the motors with variable speeds, but if I wanted that I could do it more simply and cheaply with my own circuit.

The Gertboard manual suggests that you can vary speed with pulse-width modulation (PWM), but the problem is that the Raspberry Pi only has one part-time PWM output and the switching on the GPIOs isn't fast enough for me to do botched up PWM on those. Maybe one PWM output is enough though - rather than connecting four GPIOs to the motor controller inputs, use the GPIOs to gate the PWM input - yeah, that'd work.

I'd been looking at a separate motor controller board, but wouldn't it be neater and cheaper to put a controller chip on my own board? The Gertboard has an L6203, but that's more expensive than the controller board, is specced way beyond what I need, and only has one channel whereas I'd like two. I'll have to look around and see if I can find the same IC that's on the controller board, or something similar. Would PWM work with the motor controller? Gert reckons it will with his board, and who am I to argue?

Wearing a bra on your head while working on geek
projects turns them into Kelly LeBrock. That guy
should know, he's Iron Man FFS.
Now I'm trying to remember the circuits to do some of these things, but I've forgotten pretty much all of the analogue electronics I learnt at college. Thankfully the intaweb is helping to jog my memory a bit. If I do want variable speed, gating a PWM signal should just be AND gates so that's easy. But I'll probably want something to convert that to a stable DC analogue voltage, which is... an integrator? Can the motors take a PWM input? The GPIOs are 3.3V and the motors take 6V, so I need to step that up and be able to supply up to 2A for each motor. An OP-AMP with positive feedback isn't going to supply enough current so I'll need a hefty power transistor in there... whaddya know, I just re-invented the Low drop-out regulator... Is there an off-the-shelf LDO which lets you provide a variable reference voltage? That's basically what's in a motor controller, so I've come full circle - might as well just get one of those.

As I'm so out of practice with boards I'm a bit apprehensive about doing a complicated board in one go. It's tempting to do a smaller board first, or at least do a larger board in phases and test it between each phase. Just getting battery power to the RPi via a switching regulator would be a good start and confidence boost. At the very least I've got to start getting some schematics drawn up, not worry about exact capacitor values or board layout yet, but just start turning the morass of nebulous ideas into something a bit more coherent.

Whatever I do with the robot, it's going to need some software so that's where I'm spending my time at the moment. I'm trying my hand at cross-compiling as it's going to be faster than compiling on the RPi itself, and it's also harder than a native compile so more interesting. As I've been playing with DOSBox recently I downloaded the source for that, got the Raspbian toolchain and... wait a minute, where's the Makefile? There's a Makefile.am and a Makefile.in, what're they? Turns out they're for the GNU Autotools, which I've never used before.

Learning how to use those and how to cross-compile was a bit much to take on in one go so I took a step back and started with "Hello world". Once I'd mustered the presence of mind to write 5 lines of code which actually compiled natively, I'd got the right toolchain for compiling C++ from Ubuntu to the Raspberry Pi (g++-arm-linux-gnueabihf seems the best bet), and I'd sorted out the libraries, I got an executable which I FTP-ed over to the RPi, SSH-ed in, ran the executable, aaaand.... segmentation fault, my old friend. Next time my "Hello World" is just going to print "Segmentation Fault" so it'll look like it worked whether it did or not. It's barely worth opening up gdb, with a program that simple it's going to be a linking issue so it's probably just a matter of getting the right options in the makefile.

In other news, I've continued getting things to work on DOSBox. Speedball 2 works very nicely, it sounds absolutely awful but early PC games always did. I got UFO: Enemy Unknown working briefly but it was very slow and after I'd faffed about with the config it stopped working. It looks like I should give up on protected mode games until protected mode is improved, which it probably never will be, or DOSBox starts using SDL 2.0 with its improved use of OpenGL. And it seems that others are having much more luck with MAME than I did on my half-arsed attempt, so maybe I'll give it another go one day.

Monday, 6 August 2012

We've only just begun

I finally got a chance to play with the Raspberry Pi and it's been a doddle to set up so far.

After reading some of the horror stories, I half expected some problems getting the RPi to start up at all but thankfully had none. I guess my paranoia helped as I'd read all about the power issues, purchased a half-decent power supply and a really good powered USB hub.

My only video option at the moment is composite to my telly. Yes, I know most self-respecting gadget-philes bought HD flatscreens with HDMI years ago - myself and my partner both bought 21" Sony Trinitrons over a decade ago, we don't want to get a new telly until at least one of them expires, and the damn things are just too well-built, refusing to die. I'm using a composite + phono to SCART adaptor, combined with composite and 3.5mm to phono cables running from the RPi, and the picture isn't too bad. There's a bit chopped off the left-hand side, the interlacing looks awful sometimes and text in a terminal window is a bit blurry, but it's generally good enough for most stuff and it'll do until my HDMI-DVI adaptor turns up.

Bear in mind that though I've had close to 20 years of experience as a UNIX/Linux user, I've not really done much in the way of installs or admin beyond switching window manager. I've installed Ubuntu on my netbook about a million times, but that practically installs itself. I've been learning as I've gone along, trawling the intaweb for guides, supplementing them with a great deal of educated guesswork and trial-and-error.

For the Raspberry Pi I'm using Raspbian, as it's now the recommended Linux distribution and the optimisations are coming thick and fast. At initial startup there's a config utility to help sort out the important stuff. I'm not going to be doing anything clever with graphics yet, so memory's shared 7:1 between CPU and GPU. I thought I'd give the LXDE window manager a go to start off with, but at PAL resolution it looked a bit cluttered and with the interlacing it was a little too reminiscent of Amiga Workbench for my liking, so I turned it off again and turned on SSH.

SSH just worked, with no fuss whatsoever. I got the IP address from my router (though "ifconfig" on the RPi also does the job), put it into PuTTY and that was it - remote access to the RPi. Xming was also fairly easy to set up without really knowing what I was doing - initially the RPi's IP address was being blocked so I added it to Xming's X?.hosts file, set the $DISPLAY variable, et voilĂ . Then I undid all that and set up X11forwarding in PuTTY to sort out the display for me. Wireless was a lot more tricky to set up, but once I'd read a few guides (the debian one was the most useful), convinced myself that the dongle wasn't broken, figured out that you need to be root to scan for interfaces, learnt the syntax of the interfaces file, realised that WPA needs a completely different set commands to WEP, and worked out how to generate a PSK, I was up and running.

Setting those things up meant I could remove the mouse and keyboard, disconnect the telly, and unplug the Ethernet cable tying it to my router. I've now got the RPi on my desk instead of a footstool by the telly, with just the hub/wireless plugged in. It's a useful setup for playing around, getting better stuff set up, taking screenshots, perhaps natively building C++, but what to try first? For low-effort instant gratification, how about some old games?

Tiny amount of activity in game, sshd CPU usage spikes
I mentioned Beneath a Steel Sky in my previous post, and that's free on ScummVM so I gave it a go... perhaps unsurprisingly it was very, very slow over a remote display and there was no audio. When I switched back to using the telly it was nigh perfect and really fast, huzzah! There was the odd crackle and pop over the speakers, though I've no idea whether that was the SCART connector, phono cable, socket, board, drivers, feedback from the USB hub or just the incessant ALSA underruns you get with a busy CPU. Back to the SSH connection again I could see the CPU usage of the sshd (SSH daemon) process spike when anything moved. Turning off X11forwarding and going back to insecure X?.hosts/$DISPLAY didn't make much difference, but VNC was a bit better.

It took over an hour just to get to kick-off for this
screenshot
Next up, Sensible World of Soccer running on DOSBox. First job: finding the CD, which took a while as it was in storage, in one of the many boxes of stuff we rarely use in the spare room. You may be shocked to learn that the Raspberry Pi doesn't have a native CD drive, and I don't have one with a USB connection, so I tried to find an ISO maker which was a) free and b) actually worked. Eventually I settled on ISODisk, which was a bit flakey but did the job nicely. Once SWOS was running it was ridiculously slow with Xforwarding, and didn't work at all with VNC. On the telly there was a keyboard issue, but once I'd sorted that out it was still rather slow. It could probably be tweaked a bit, but SWOS runs in protected mode which is notoriously slow on DOSBox so I think the RPi's 700MHz processor doesn't quite cut it. That's a shame, I was quite looking forward to having SWOS on a little box under my telly - though I've discovered that there's a Wii version of DOSBox available...

I've also had a look at MAME, but there's some weird video driver issue I'll have to get to the bottom of. If I do get it working, I'll really want to get my gamepad working too but that looks a bit tricky under Linux. It's a Microsoft gamepad, so what are the chances of an official Linux driver?

Perhaps trying to use the Raspberry Pi as an emulation machine is a bit optimistic as its processor isn't great. All the graphics is going through the CPU, most emulators don't take advantage of the GPU. ScummVM works well but as I understand it that's not really an emulator, it's a new implementation of the interpreter. But it's been a useful exercise in just getting stuff working and probing the limitations of the box. For my next trick I think I'll try something a little more suitable. Maybe I'll learn how to use OpenGL ES? Well lookie here, the Quake II source code...