In order to see the Flickr control that goes here, please install Microsoft Silverlight!
Get Microsoft Silverlight

Ideal Binary

by Kevin Doolan 4. August 2009 17:49

Gravity Wave FREE has been submitted to Apple.  The premium version will be submitted within a week.  I already have at least two major feature updates planned, but I'm going to hold off implementing until the premium version is in the pipe.

By sheer coincidence, my brother has also completed the first version of our new corporate site, idealbinary.com.  He'll be joining me full time from September.

I'll be keeping this blog for non-work related posts, some code related and some not so.  I'll be covering anything work related on my Ideal Binary blog.

Gravity Wave

by Kevin Doolan 25. July 2009 22:10

After about four months worth of development time I’m nearly ready to submit my first full App for the iPhone.  It’s called Gravity Wave and it is loosely inspired by the old 8-bit classic, Thrust.  There have been many, many Thrust clones over the years (and there are already a few on the iPhone) but most tend to be slow, precision-movement type games.  Gravity Wave is a little different because it opts for a more arcade, fast pace type of game play.

I used Gravity Wave to help drive the development of my middleware platform UtopiaGL.  The first draft of UtopiaGL was completed at the end of December last year, and over the last 6 months or so I have been refining and further developing it to along side a few different projects, including Gravity Wave.

Gravity Wave is a pretty straight forward game.  I deliberately kept it simple because I knew I would be doing everything myself, except the title music and sound effects, which I licensed from a sound production company.  Essentially that means every task was on the critical path, pretty much.  That’s not an ideal situation, but this was very much an experimental project.  Given the super saturation of Apps on the App Store, I’m not sure how this project will do commercially, so I’ve done my best to keep my costs down, while maintaining the quality level as high as I could.

Below is a sneak peak of the game.  I’ll post further on technical aspects of the engine over the coming weeks. 

Level 1

Level 1

Level 1

Level 1

iPhone App Development

by Kevin Doolan 25. July 2009 21:59

I’m entering the final sprint of my current iPhone App project, so I thought I’d post some notes on the practices used (and some I should have used much more!) during its development.  I’ll post further on the low-level development, submission and review process later. For now I’ll focus on high level development issues.  These points are not iPhone specific but the ecosystem of the App Store makes them particularly relevant for a lot of the start-up, one-man band or small indie team projects that are buzzing at the moment.

Define Your Purpose

It sounds so obvious, and yet plenty of projects flounder for lack of it.  Begin by explicitly defining your purpose.  Be as specific as you can be.  The very first step you take in any endeavor is the one that establishes your direction.  Every single move you make should be in agreement with your purpose.  All those mini decisions you make every hour, week, month and so on have a compass.  If you don’t define your purpose, those compasses can drift without any clue where they should be pointing – the odds of all them pointing in the right direction are arbitrary and extremely unlikely.  Defining your purpose establishes your polar north and gives each one of those decisions a shared context and allows them to proceed intelligently, as if in concert. This applies as much to individuals as to teams.  Always define your purpose, and refer to it regularly.

Prioritize

You can’t do everything.  You have limited time, resources and energy.  Make a list of what you need to do, and sort it by priority – what do you vitally need to do and what can you live without?  Be prepared to drop features.  Establish up front which ones are core, and which ones you can drop later if you need to.

Be Realistic

If you’re working on your first iPhone project, keep it small, simple and straight forward.  It’s very easy to bite off more than you can chew.  The smaller your team is, the more sensitive you’ll be to schedule overruns.  Keeping things simple helps minimize this.  Use your first project as an intelligence gathering exercise.  You want to find out the full extent of the pipeline of going from an idea to a finished App on the deck.  If you can’t complete a simple project, you can’t complete any project.  Don’t let your App turn into Duke Nukem Forever Never.

Use Agile Practices

This is really a no-brainer for any software development project.  If you have not had the good fortune to have tried out proper Agile dev practices, stop what you’re doing immediately.  Pick up Agile Principles, Patterns, and Practices or any of the many great books on the subject and dig in.  The ultimate payoff is this.  Clean, simple, safe and focused code, regular working builds that actually do something useful, and best of all low stress levels even in high paced dev cycles.  Pick a practice, say Test Driven Development, and start using it.  When you’re comfortable with it, pick another.  Make Agile practices second nature and you’ll never look back.  If you’re like me, it’s not that difficult to occasionally slip back into old-school habits, particularly if you’re working on your own. Resist!

Get to the Heart of the Matter, Quickly

Whatever your app is supposed to do, make sure it can do it as soon as possible in stub form.  If it’s a game, then you should be able to play it early in the schedule, not late.  Use content placeholders. Don’t leave the core feature of your app until the last few weeks of your development schedule.  Any kind of app needs time to settle into it’s final shape.  You rarely see this happen immediately – it takes time, attention, testing and iteration.  Write your code to facilitate this.  For example, I wrote a Finite State Machine compiler for UtopiaGL which allowed me to flesh out the overall app flow in literally 30 minutes.  On day one of my current app, I could navigate through all screens and menus (splash screens, main menus, sub-menus, level selection, in-game etc).  The same process was used for game play, and all entity behavior.

Procedural Content

One of the best ways to cut your content creation time down is to make good use of Procedural Content.  It will depend largely on the type of app you’re working on, but many apps can benefit in some way.  Terrain generation and texture generation are the usual things you hear about within game development, but you can apply procedural methods to pretty much any type of content.  Ninety percent of the content in my current app is procedurally generated (most of it off-line).  The world terrain was generated by a mixture of hand made level layout images (a simple two-color image mask that identifies where a player can go) which feeds into a noise based terrain texture and geometry generation system.  The net result is a terrain that exactly matches the game-play needs, but looks like typical landscape.  I also used code to create some of the regular geometry using basic primitives.

In-App Editing

My current app is a simple arcade game.  I’m using it to flesh out my middleware engine and ensure that it’s as flexible as it can be.  I wanted the game-play creation cycle to be as quick and friction-free as possible, where you can make changes to levels and immediately test the game-play.  Rather than creating a separate editor I opted to implement editing in-game.  For the windows build of the App (I’ve done 99% of the work in Windows) you can play any level, hit the on-screen edit button and go into editing mode.  You have full editorial control over all entities (placement, attributes etc), and an on-screen edit palette that allows you to create, drag and drop, zoom, pan etc and otherwise manipulate the scene.  When you’re happy with your changes you hit commit and you’re back to normal game-play.  Each level was drafted in less than an hour. I further play-tested over the space of a few days, and that’s still on-going with minor tweaks here and there.  The typical cycle was:

  1. EditMode: Layout overall level structure. 
  2. PlayMode: Test overall timing. 
  3. EditMode: Flesh out a small section of level up to a check point. 
  4. PlayMode: Test game play up to check point. 
  5. Repeat steps 3 and 4 until level is fleshed out fully. 
  6. PlayMode: Play test the entire flow and timing of the level.

Before editing commenced I established the overall design goals of each level, so that I could work through them fairly quickly and still have them fit into the overall scheme of the game.  There’s a very interesting latency issue with any kind of creation process.  There’s a timing ‘sweet spot’ where the whole process flows very smoothly provided you can get the details out of your head and onto the screen at a rate fast enough to move with your thoughts, and not be held up waiting for something (like having to quit an editor, compile content, load into the game, navigate menus, load a level, move to the point in the level you’re interested in, and…what was I doing again?!?!).  It would have taken considerably longer if I had opted for offline editing. 

There is also another very important creative aspect: feedback.  When you plan on a certain type of game play, it usually doesn’t translate into reality just the way you expect – there will be things you didn’t anticipate.  I really enjoy watching this process happen, because you can usually find a wealth of opportunities you overlooked initially.  Some things you thought would be fun may not be, and something you never considered important may end up being your game-play center piece.  You can only capitalize on this if you get game-play working early.

If you’re contemplating creating editing tools yourself, whether in-app or not, think carefully about how they should work.  Consider the workflow of the artist or the designer in detail, how much time each step will take, how much effort is involved, what can be automated etc.  Multiply your timing estimates over the expected creation cycle timeframe, and identify the stages that should be focused on to increase production speed.

Pragmatism

This is something you need to get clear to ensure the best chance of lift-off for your project.  Programmers often slip into a frame of mind where they want their code to be spotless.  I don’t sleep well when I know there is a mess waiting for me the next time I sit in front of my machine.  The problem is, it’s impossible to make things really spotless – everything has a context in which it is judged and that context is a moving target.  This can turn into an enormous tar pit.  Avoid it.

You can deal with this by referring to your project’s purpose and by keeping Agile.  Does the work I’m about to do agree with my purpose?  Will cleaning up this class, writing a general purpose solution, etc, get me closer to my goal?  If the answer is “no, but…”, then it’s probably time to stop and have a think about what you’re Real purpose is, because it’s not what you’ve convinced yourself it is.  Use Refactoring (to allow your code to change quickly, dramatically, and manageably), TDD (to help keep the design clean and guarantee changing or refactoring your code doesn’t break anything), User Stories (to keep User focused, and avoid wasting time writing code that doesn’t help your project) and short iterations (to help establish your pace, and allow better forecasting of completion times) to ensure you keep on track and avoid getting stuck in this pit.  Do the least amount of work to get to your goal.

If you’re writing an app to be used by people then…you’re writing your App for a User.  That clarifies everything you need to know.  You want things to be perfect?  That’s great.  Don’t try to make your code perfect.  Try to make the App perfect instead.  Ideally, when the User uses your App they should see as close to Perfection as you can create with the time and resources you have.  When a programmer looks at your Apps source code they should see Pragmatism.

If you’re using Agile practices, then it’s ok to have parts of your code appear a little messy by traditional standards – it’s the quickest way to get your User Stories implemented.  Short, simple code is beautiful.  When you start needing to revisit that code, or repeatedly duplicate it, or require more flexibility, then (and only then) you generalize and clean it.  It’s ok, because you have automated tests that constantly watch that code for bugs. It’s ok because refactoring allows you to radically change the code quickly and in a stable way when you need to.  When you leave your work at the end of a day, you know with a high degree of certainty where you are on your schedule, how stable your work is, and that with one click you can spit out a release you could put in someone’s hands and have them use the App.  You also feel far less stress – one of the most welcome side effects of Agile practices.

Pragmatism means writing solid code, not perfect code.  Perfect code usually means Indulgent Code – someone indulging their appetite for elegant, sophisticated, complex-toy kind of code, often at the expense of clarity, time, usefulness etc – this kind of code is absolutely not for the Users benefit, it’s for the Coders ego, or misplaced idea of what good code is.  Indulgent Code is what happens when a developer gets so focused on the code that the end result, the App they’re developing, gets completely overshadowed by the act of coding.  This is detrimental, and usually forecasts failure for the project.  Ironically, shifting a perfectionist focus off the code and onto the App by using robust practices is when the code actually starts to approach the only measureable perfection you can humanly achieve.

Conclusion

If you’re aiming to put applications out on the iPhone then you’re up against some pretty stiff competition.  The sheer numbers of competing Apps is daunting to say the least.  It’s more important than ever to try to minimize development time and maximize quality.  The practices you use can make or break your project.  I benefitted from using Agile practices on my current project, but I could have used them more fully – something I intend to do on my next project.  I can’t recommend them enough.

iPhone OpenGL ES Performance

by Kevin Doolan 6. March 2009 01:30

Fine tuning 3D code can be a black art, particularly if you are new to it.  If you’re not familiar with the hardware, it may not be immediately obvious why certain calls are expensive and others aren’t. To make matters more complicated, each system has little quirks and gotchas that surface along the way while developing 3D code.  The iPhone is no exception.  Here are some optimizations that may save you some head scratching, particularly if you’re new to OpenGL.

EDIT: I have updated some of the points below to reflect the iPhone benchmarking done by Patrick Hogan. Thanks again Patrick. For more details, check out the comments at the bottom.

Timers are Great, Threads are Better

The ‘Hello World’ iPhone OpenGL ES project sets up a simple timer based run loop, which is a very convenient way of executing your app.  You don’t have to worry about synchronization issues, there’s no overhead involved in context switches (your app is executing in a single thread) and debugging is trivial.  So why would you want to rework your code to operate in a separate thread?  The reason is simple:  With timers there will potentially be a gap of ‘dead time’ where your engine is doing nothing, waiting for the next timer event to fire.  This simply defeats the potential parallelism that you can achieve between your app and OpenGL.  The GPU has enough to cause it to stall when interacting with your app, without the additional delay of your app also waiting for a specific time in which to execute.  As soon as there are CPU cycles available to your app, you should take them immediately, get all frame processing out of the way and issue your calls to OpenGL.  From a performance point of view, ideally neither the CPU, nor the GPU should ever be stalled if it can be avoided.  My engine, UtopiaGL, runs around 15% faster using a thread based frame update instead of a timer based approach.

Avoid thread synchronization issues by engineering your code to simply not need synching.

EDIT: My current project has turned up some new details here. When I tested on generation 1 hardware, the threaded approach resulted in sporadically choppy update.  I was able to address it by introducing a short sleep per frame, but it ended up being the same as the timer method in terms of performance.  It looks like the OS background tasks get starved of CPU cycles and then all of a sudden decide to shut your app out for a relatively huge chunk of time while they look after themselves.  This feels very Brew 1.1!!  The timer method is far simpler, so for generation 1 hardware I opt for the timer approach.

Allow OpenGL to run in Parallel

The OpenGL ES pipeline executes in its own thread.  When you issue calls to the API, they are not executed immediately, and instead get placed into a command buffer which eventually gets flushed.  Calls to the API return immediately, allowing your app to proceed with whatever it’s doing.  This means that in theory you should be able to execute for a considerable chunk of time in parallel with the graphics hardware.  This is as you would expect, but there are certain calls you can make that effectively break this parallelism, and with them, the frame rate of your app comes spinning down.  The usual suspects are glReadPixels, glTex(Sub)Image, glBuffer(Sub)Data etc.  If you need to use them per frame, be aware they have quite a performance hit. 

If your app runs within a single thread (i.e. the renderer) then avoid calls to glFlush and never, ever use glFinish.  glFinish is particularly bad.  It flushes the command buffer and blocks the calling thread while it does it.  glFlush does the same thing, less the block.  Both are used to synchronize with the GL driver, but you rarely need to do that.  An example of when you might need it is if you had two rendering threads, both of which were rendering into the same GL context.  If you do need synchronization, use glFlush.  Pretend glFinish doesn’t exist – you should literally never call it on the iPhone.

Use Vertex Buffer Objects or Not!

Make good use of Vertex Buffer Objects for any static geometry.  In theory this allows you to store geometry (including index data) in fast video memory.  The iPhone uses shared memory i.e. the CPU and PVR hardware share the same memory, so in this case it simply means you save having to upload your geometry to GL every time you call glDrawElements etc.  The savings here can be very significant.  You would be amazed how much geometry you can throw at the iPhone with VBOs before it breaks a sweat.

EDIT: After reading Patricks comments below, I went back and looked at my VBO code. Unfortunately the gl driver doesn't take advantage of VBOs and uses a full copy operation in both the VBO and non-VBO case. Up until now I’ve been working off observations with my own shader pipeline. I see an increase in speed using VBOs but from examining it more closely, the speed increase doesn't come from the fact that the vertices are in a VBO - it comes from the fact that my non-VBO path does more work on the CPU side. This surprised me because I saw a drop of around 8fps with a stress-test scene in the non-VBO case - quite a bit more than I expected from the extra CPU work alone – I expected it would be in line with VBOs being effective, and that things would run slower without them. My shader pipeline works like this. Geometry is queued for rendering, and when the scene is complete it tries to minimize the number of calls to glDrawElements by rendering from shader buckets. Effectively each shader has a geometry bucket and any geometry in the same reference frame is packed together and issued to gl in a single call to glDrawElements. Most of the geometry (anything not dynamically generated per frame) lives in its own VBO. In this case I don't add it to the shader bucket; I just render it on its own with a call to glDrawElements. Since VBOs don't seem to offer a speed increase, when I turn them off and fall back to my packing scheme, it should be fairly similar in speed – but it’s not. It’s doing the same amount of work as the VBO case plus the extra work I’m doing to pack everything. There seems to be no advantage in minimizing calls to glDrawElements - at least not with the scenes I’ve been testing. It seems it’s better to just issue multiple glDrawElements calls. I didn't look at it too closely originally because my typical scenes were running at 60fps - I saw what I was expecting to see: that VBOs were faster – in my case they were, but not for the reason I assumed.

The tests I did above were not exhaustive and are very particular to my engine and current app, but they agree with Patricks findings below. When I remove the code to minimize glDrawElements calls, and issue the same geometry with and without VBOs I see no real difference in speed. Vertices have xyz, tc and color attributes – lighting is precached.

Be Cache Aware

Use indexed striped triangles for geometry, and render through the glDrawElements call.  Sort tris to maximize vertex cache usage, then sort vertices to be in sorted-tri-order.  Don’t worry too much about having the tris in strip order – make sure cache usage is your sorting metric.  Where it makes sense, interleave vertex attributes, for example all static geometry should have interleaved attributes.

EDIT: I haven’t confirmed it in my engine yet but it seems strips do outperform lists by a small amount.

Read the Apple and PVR performance guidelines

In short: RTFM.  There’s a lot of good advice in those docs.  Surprisingly, when I last checked, Vertex Buffer Objects weren’t mentioned in the Apple guidelines – an odd thing to omit.

EDIT: In light of the VBO implementation, this isn't an odd thing to omit at all. Also, the Apple recommendation to use lists instead of strips doesn’t seem to hold water, although it is fairly close. See the comments below from Patrick. The best advice is: Read the guidelines, but profile your code.

Use Instruments

Instruments is an excellent app that comes with Xcode and allows you to profile your app running on-device.  You can examine all kinds of details about how your app executes, and zero in on bottle necks.  With respect to OpenGL, you can use it to see which calls are being issued a lot, and/or take the most time.  This is an invaluable tool, and should be your guide for any optimizations.

Conclusion

Every app is different, and even the same engine could perform wildly differently with different content, so when you hit a bottleneck you really need to profile to find out where your app+content is spending most of it’s time, and focus your optimizations there.  That said, many of the above points apply globally, and your app will almost certainly avoid performance pitfalls if you are careful to adhere to them.  The iPhone has some serious horse power under its hood. Initially I was skeptical about just how powerful it was, and after fine tuning my engine I’m extremely pleased with it.  Compared to other mobile 3D devices, the iPhone and iPod Touch are in a class of their own.

iPhone 3D Engine Development

by Kevin Doolan 24. January 2009 03:16

Last month I completed my middleware 3D engine, UtopiaGL for iPhone.  I’d never actually used a Mac before this project and I wasn’t sure what kind of effort would be required to get up to speed on OSX and the Mac development process (short answer – about 2 hours of effort!).  Here are some notes on my experience.

EDIT: I recently published a simple game on the App Store using my engine.  It’s called Gravity Wave and you can check it out here.  I will be blogging on my company site about how it was created, and how UtopiaGL made it straight forward to produce, so check out the site soon...

The Engine: UtopiaGL

It’s a C++ OpenGL ES, Shader-based engine and tool chain similar in feature-set to PC Games from around 5 years ago.  All OpenGL ES features supported by the iPhone are exposed through the Shader system, so you can do anything the PVR hardware can do.  The pipeline is optimised according to the PVR recommendations, as well as my own testing.  The helper tools include a Shader Compiler, Font Compiler, Model Compiler, 3DS Exporter and State Machine Generator.

Development

The core engine and tools took 8 weeks to write.  I wrote it in C++ on Vista using an OpenGL ES emulation layer.  I’m very familiar with Visual Studio – Xcode is cool, but at the time I just wasn’t comfortable with it so I battled on in Windows.  I wrote the engine to be portable across OS platforms so it has a System layer to abstract away the Platform specifics.

Once I had the engine ready and running well on Windows I moved it over to Xcode and rolled up my sleeves, expecting quite a bit of work.  Amazingly, it only took a few hours to get working, and most of that was learning the ins and outs of Xcode.  When I ran it on a device using a test scene it initially ran at 30 fps.  I was pretty disappointed at that speed given the optimisations that were being used.  I took a quick look over the code and saw that the Framebuffer was being created as a 32bit RGB buffer – I had just taken the setup code from the OpenGL ES Hello World app to get up and running.  I set it to RGB565 and sure enough it shot up to 60 fps.  It does drop down to 40/45 fps when you turn on Framebuffer effects (like Bloom glow).

Edit: After a few weeks of further optimisations and testing I've managed to squeeze quite a bit more GL performance out of the device (to my sheer astonishment).  For example Framebuffer effects now run at a constant 60 fps, details coming soon...

Xcode

I wasn’t sure what to make of Xcode initially.  After getting used to it I’m quite happy with it as a development environment.  There are a few things that feel a bit minimalist about it – it doesn’t feel as advanced as Visual Studio, but at the same time, you do feel like you’re getting more signal and less noise, considering what’s there.  (Actually Mac OSX in general feels quite minimalist, in a good way.) One thing I find annoying is there is no clean, simple way of including one iPhone project in another (this has been the single biggest influence on my view of Xcode).  VS does this extremely well with ‘Solutions’ which wrap Projects.  Obviously when you’re writing a middleware engine this is something of a requirement.  Right now I’m including all the engine source directly in the Application projects in Xcode, unlike in Visual Studio, where they have a neat Application Solution that simply includes the engine library project.  You can get Xcode to play ball, and a few people have blogged about it, but I’m not happy with the way it works – it should be a trivial thing and it certainly is not – it involves many, many steps, when it should require only one: inclusion of a reference to the library project.

EDIT: When I wrote the above, there was no Static Library project template in Xcode, and I didn’t know enough about the process on Mac to set it up quickly.  The Static Library project template does exist now (and has done for a while) and works extremely well, allowing exactly the same library project dependency set-up I was used to in Visual Studio.  Thanks to Simon for pointing me towards this in the comments to this post.

Objective C

If you haven’t written Objective C before, it can look a bit weird at first if you’re coming from a more traditional C derived language background.  It is growing on me, and I really do like the fact that the Apple Framework APIs look super clean.  I just have one problem with it – the way parameters are interleaved with Message names.  The idea is sound, but I don’t like the way it’s done in Objective C.  Many languages have done it well and they do it by allowing you to name parameters as you pass them, like MessageName( FirstParam=47, SecondParam=”Hello” ).  The reason I’m not keen on the Objective C way of doing it is I find I need to stare at the code to extract the meaning – it takes effort.  This is something I’ll probably get used to, but right now it’s a bit annoying.  I think it’s that there are no brackets delineating the parameter list – my eyes are scanning for it and can’t find it, making it a bit jarring.  If I had been proficient with Objective C and moved to C++ I might find C++ equally jarring.  I’ve learned many new languages over the years, but I’ve never found any so odd – not even Lisp or Prolog.  Maybe it’s just me…

In any case, I was able to avoid Objective C almost entirely.  The only Objective C is in the Platform code to set things up, drive the message pump, and provide implementations of system functions like getting the App path.  Maybe 200 lines of trivial code.  Everything else is portable C++.

Memory Management

Garbage Collection has been disabled on the iPhone (a good thing – full blown Garbage Collection is expensive, and arguably a nonsense feature to have on a mobile, power constrained device). I wrote 3 memory management systems for UtopiaGL, all very simple, and very specialised.  The first is for the core engine back-end which is very light, extremely fast and results in zero fragmentation.  It’s not Garbage Collected but it does allow you to nail memory leaks immediately.  You can also use it in the front-end Application code for making gross allocations, like loading large App-specific data.  The second one is exclusively for the Geometry processing engine and is super simple, and extremely fast – it’s an allocate-and-forget system designed for permanent allocations.  It gets zapped entirely at the end of each scene render.  Lastly, there’s the client memory manager (the client being an Application written using the engine).  This is designed exclusively for the front-end client code to use and is Garbage Collected (reference counted, but detects cyclic refs).  It’s also extremely fast (nowhere near as expensive as power-hungry Mark and Sweep GC for example), and is actually a relatively tiny piece of code.  When objects are no longer referenced they are deleted immediately unlike traditional GC.  Objects allocated with this system are only visible through reference pointer objects and reference pointer array objects (like in Java or C#).  You can create arrays (even multi-dimensional arrays) of object references that behave exactly like the Java or C# equivalents, complete with a .length member per dimension.  You never have to explicitly delete anything, space permitting. All allocations go through the ‘placement’ new operator, so it is very comfortably integrated with C++.

Touch UI and the Accelerometer

These are the most interesting new features you get to play with.  The Accelerometer is impressive – it’s extremely sensitive, way more than I was expecting.  It provides a 3 vector with force in each spatial dimension – simple and to the point.  You can control the rate at which the accelerometer feeds samples to your app, but for most interactive apps you’ll want that to be as close to 60Hz as possible.  In order to extract meaningful information from the data coming from the accelerometer you will eventually need to apply some kind of filtering.  Some knowledge of Digital Signal Processing is very useful here, but not required (a High or Low Pass filter can be written in a few lines of code and is very straight forward to understand).  The other input method is of course the Touch interface.  Your app gets a handful of messages informing it of touch events (when they start, move, end etc).  Each individual touch is tracked, so if you can imagine pressing your finger on the screen, that creates a ‘Touch’ object, if you move your finger that particular Touch object gets updated with new position information and a ‘Phase’ field that reflects the current phase of the touch event: Began, Moved, Ended etc.  When you finally lift your finger, the Touch object gets it’s Phase set to Ended and after you have been informed about it, the Touch object gets recycled by the system.  One Touch object exists for each point of contact on screen and is updated as its point of contact changes.  Tap events are modeled as touches also, with the tapCount field indicating the number of taps that have occurred.

I have abstracted Touch events essentially identically in UtopiaGL, except each Touch can be identified with an ID. UtopiaGL Touches can track several seconds of movement (you can configure this), unlike the raw events which just give you a current and previous position.  This eases the burden on Gesture Recognition somewhat.

UtopiaGL has it’s own event system, so in the platform specific code, native events are translated and fed to UtopiaGL’s system object in a format it recognises.  From there, they are distributed to the rest of the engine in an entirely platform independent way.

OpenGL ES on iPhone and iPod Touch

The features exposed by the PVR hardware are excellent.  There was one disappointing omission and that was the Vertex Program extension.  The PVR hardware supports Dot3 blending which means you can do Bump/Normal mapping.  Unfortunately, without the Vertex Program extension you are forced to enlist the CPU if you’re doing Tangent Space bump mapping, with a matrix multiply per vertex.  If you’re doing Object Space bump mapping you don’t need to do that, but you are stuck with rigid models that can’t deform without breaking their lighting.  Another reason why I was hoping the VP extension would be exposed is to supply my own lighting equation.  The standard OpenGL ES lighting system is expensive.  All you need in most cases is a very trivial ambient + diffuse lighting equation.  Without the VP extension you either bite the bullet and use the Standard OpenGL ES lighting model (which allows you to store your geometry in video ram) or write your own simplified lighting code and upload the vertex colors per lighting change.  If your models require CPU work, for example if you’re applying some kind of CPU-based deformation to them then it may make sense to implement the lighting on the CPU and upload everything in one go.  If your geometry is entirely static, then it may make sense to just use the OpenGL ES lighting model.  It’s not a clear cut situation. Right now I’m using the OpenGL ES lighting pipeline for all lighting, but I have left a stub for CPU based lighting – I’ll be experimenting with this shortly.

EDIT: The shared memory model on the iPhone and the fact that the VBO extension offers no speed up really mean you're faced with a more level playing field: you just operate off system memory vertices.

The Framebuffer (FBO) extension is supported, and actually the primary way you render to screen.  The FBO extension opens up a wealth of possibilities (using render-to-texture) and I was delighted to see it on the device.

Performance is excellent, above what I was expecting from a non-dedicated games device. 

UtopiaGL Shaders

The Shader system is a pass-based renderer, which is configured by a compiled shader script.  The compiled shaders are tiny, usually around 150 bytes.  The reason I compile them offline is to remove the burden of parsing them on-device at run time.  They’re not a million miles from Quake3 shaders, but they give lower-level control, for example you can fully control multi-texture (2 TMUs are expected) and the texture combiners.

I wrote a packing system for vertex attributes to minimise VGP and CPU cache misses.  It’s very straight forward and works like this: Shaders expect certain attributes to be present in a vertex in order to execute, e.g. if a shader does multi-texture mapping and lighting then it needs XYZ, Normal, TC0 and TC1 attributes.  You have two options when representing these attributes in ram.  You can either have a Structure of Arrays (SOA) or you can have an Array of Structures (AOS).  SOA is conceptually easier.  Essentially you have an array of XYZs per vertex, an array of Normals per vertex and so on.  This has the advantage that if you are performing CPU-based deformation on any attribute array, you limit cache misses.  You pay for it when the GPU gets to work though, because the CPU cache is under-utilised as it pulls vertex information from very different locations in ram.  The alternative is to interleave the attributes ala AOS.  In this case you have and array of vertex structures, each of which has the XYZ, Normal, TC0 and TC1 attributes.  This means you under utilise the cache if you need to perform CPU-based deformation on any attribute, but it means the cache is well utilised when the GPU is pulling in vertices.  My solution to the problem was to not use either method, but a hybrid of both.  Any static, non-changing attributes get interleaved into a per-vertex structure.  This doesn’t incur any cache abuse w.r.t the CPU because it never looks at them and the GPU maximises cache hits as it pulls in the vertices.  Any volatile attributes that need to be processed by the CPU are arranged into arrays for quick processing and I take the cache miss on the GPU end which is essentially limited to those specific attributes.  You get the best of both worlds.

Before the geometry ever gets to the Shader system, I apply a reordering algorithm to both the triangles and then the vertices to ensure maximum VGP cache usage.  It’s a fast process and is performed offline in a Model Compilation tool which is part of the engine tool-chain.

EDIT: There seems to be an advantage to using strips with a call to glDrawElements: see my more recent post on gl performance.

One thing I found odd about writing the shader code was the PVR compressed texture support.  PVR compressed textures appear flipped along the y axis – they load upside down!  The apparent reason for this is to maintain consistency with the render-to-texture support.  That doesn’t make any sense to me!  Anyway, our lives are now slightly harder - that’s how it is, so unfortunately you need to add an entirely redundant step into your content build process to manually flip textures before PVR-compressing them, or resort to other hacks as you load the textures. Bad smell.  Apart from that oddity, the compression is excellent and the quality is likewise impressive.

Conclusion

iPhone development has been fun so far.  I’ve done a lot of work in mobile game development, and the iPhone is easily the best thing I’ve ever experienced in a mobile device.  I’ll be submitting my first applications built using UtopiaGL to Apple soon, with a little luck.  Time permitting, I will blog about specific aspects of the engine in detail and iPhone development in general.

Continuous Integration: CC.NET and TeamCity

by Kevin Doolan 2. October 2008 20:59

Introduction

I recently needed to set up a Continuous Integration server for some projects I'm about to start.  There are quite a few options, but the two main players I looked at in .NET land were CruiseControl.NET (v1.4) from ThoughtWorks and TeamCity (v3.1 and v4 EAP) from JetBrains.  I started my evaluation by taking a project I'm working on, which has a clean, one-click MSBuild powered build system (builds, runs tests, and generates both test and code coverage reports), and hooking it up to each CI server.  Below is an overview of my experience setting each system up and my first impressions.

Project

Before diving into the nuts and bolts of CC.NET and TeamCity here's a quick overview of my project.  It's a Silverlight app which has several assemblies, including NUnit test projects.  Initially I investigated using NAnt to drive the build but after looking at MSBuild I dropped NAnt immediately. Using MSBuild allows you to use the vs2008 .sln and .csproj files directly so you get exactly the same experience building outside vs2008 as you do inside - this makes NAnt scripts mostly redundant.  There are many more NAnt Tasks available, but if you really need them you can kick off NAnt from an MSBuild script, which leverages your already existing MSBuild files. You don't need vs2008 installed to run the build.  The .csproj files essentially are very similar to NAnt scripts with some hooks to allow you to customise the build process (pre and post-build targets).  Currently I have tapped the post-build target of the NUnit test projects to kick off NCover to run the unit tests and produce both test and code coverage reports using a Conditional construct in the target tag (if a property RunTests is set to True, the target executes).  This is enough to integrate very easily with CC.NET, but with TeamCity I needed to create an extra build script which executes the build above and has a further target to run NCoverExporer (to build the Coverage Report in xhtml) and MSBuild Community Task XSLT using NUnitReport.xsl (to build the Test Report in xhtml).

The AfterBuild targets in each of my NUnit test .csproj files looks like this:

   1: <Import Project="..\..\UtopiaTest.targets" />
   2: <PropertyGroup>
   3:   <CoverageAssemblyName>Utopia</CoverageAssemblyName>
   4: </PropertyGroup>
   5: <Target Name="AfterBuild">
   6:   <CallTarget Targets="RunTestsAndCoverage"
   7:               Condition="$(RunTests)=='True'"/>
   8: </Target>

The CoverageAssemblyName property tells NCover what assembly to run coverage over for this particular project assembly.  It imports the following reusable test and coverage target:

   1: <Project ToolsVersion="3.5"
   2:          DefaultTargets="RunTestsAndCoverage"
   3:          xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   4:  
   5:   <PropertyGroup>
   6:     <NUnitPath>$(MSBuildStartupDirectory)\3rdParty\Externals\NUnit\</NUnitPath>
   7:     <NCoverPath>$(MSBuildStartupDirectory)\3rdParty\Externals\NCover\</NCoverPath>
   8:     <NCoverExplorerExtrasPath>$(MSBuildStartupDirectory)\3rdParty\Externals\NCoverExplorer.Extras\</NCoverExplorerExtrasPath>
   9:     <NUnitReportsPath>$(MSBuildStartupDirectory)\TestReports\</NUnitReportsPath>
  10:     <NCoverReportsPath>$(MSBuildStartupDirectory)\CoverageReports\</NCoverReportsPath>
  11:   </PropertyGroup>
  12:   <UsingTask TaskName="NCoverExplorer.MSBuildTasks.NCover"
  13:              AssemblyFile="$(NCoverExplorerExtrasPath)bin\NCoverExplorer.MSBuildTasks.dll"/>
  14:   <Target Name="RunTestsAndCoverage">
  15:     <Message Text="##teamcity[progressStart 'Running Tests and Coverage on $(AssemblyName).']"/>
  16:     <MakeDir Condition="!Exists('$(NUnitReportsPath)')"
  17:              Directories="$(NUnitReportsPath)"/>
  18:     <MakeDir Condition="!Exists('$(NCoverReportsPath)')"
  19:              Directories="$(NCoverReportsPath)"/>
  20:     <Exec Command="regsvr32 /s $(NCoverPath)\CoverLib.dll"/>
  21:     <NCover
  22:       ToolPath="$(NCoverPath)"
  23:       LogLevel="Quiet"
  24:       WorkingDirectory="$(MSBuildProjectDirectory)"
  25:       CommandLineExe="$(NUnitPath)bin\nunit-console.exe"
  26:       CommandLineArgs="$(OutputPath)$(AssemblyName).dll /xml=$(NUnitReportsPath)$(AssemblyName).Results.xml /nologo /nodots"
  27:       CoverageFile="$(NCoverReportsPath)$(AssemblyName).Coverage.xml"
  28:       Assemblies="$(CoverageAssemblyName).dll" />
  29:     <Exec Command="regsvr32 /u /s $(NCoverPath)\CoverLib.dll"/>
  30:     <Message Text="##teamcity[progressFinish 'Running Tests and Coverage on $(AssemblyName).']"/>
  31:   </Target>
  32:  
  33: </Project>

Incidentally, editing a vs2008 project file in vs2008 is easy - just right click on the project in the solution explorer, select Unload Project, and right click again and select Edit.  When done, save your changes, close the file and right click again, selecting Reload Project.

CruiseControl.NET

Setting up was a little more involved than I expected.  You need to have IIS installed before you install CruiseControl .NET.  If you install IIS after CC.NET, you need to go in and manually set up a virtual directory. Once everything is installed, you need to dig in to a xml config file to set up how your build is pulled from source control and executed.  One gotcha with CC.NET and subversion is that initially it doesn't work!  CC.NET fails to pull the source from svn complaining about a Certificate problem. You can solve this by using TortoiseSVN to manaully pull the source down and "Accept Forever" the certificate. Feels a little hacky. You can also specify the various artifacts generated by your build that you want published, like the xml output of NUnit, NCover, or a host of other tools.  The great thing about CC.NET is that's all you need to do to get reports presented for your build.  Under the hood, CC.NET has .xsl transformation files for all the usual suspects, so once it knows where to look for these xml reports, it can do the rest.  That said, the .xsl files that come with CC.NET don't produce pretty reports.  The reports generated fit quite consistently with the overall look of CC.NET -  think web circa 1996!

TeamCity

TC was a breeze to set up, initially. Once installed, you create a project and configurations by walking through a web UI setup dialog where you provide the same kind of details as the CC.NET xml config file, so you don't need to get your hands dirty with xml.  Literally, a few minutes after installing I had my build working in TC.  However, there's no such thing as a free lunch in TeamCity .NET land.  I tried a few Build runners (MSBuild, sln2008 and commandline) but the only one that allowed me to incorporate test reports without any extra work on my part was the sln2008 runner which has a complementary NUnit task allowing you to specify your test assemblies.  This is great, but if you want to run NCover, you're out of luck.  If you use any of the other Build Runners mentioned, while you can run tests yourself and generate test output, you need to manually transform the xml into xhtml and dig into the TC xml config file to tell TC about it.  The process isn't exactly painless if you're new to this.

I used NCoverExplorer to merge my coverage reports and transform them into a xhtml summary file.  TC is already configured to expect a coverage report in the form of a zipped web site called coverage.zip at the project root - you just need to build that file, and register it as a build artifact.  This gives you a Coverage tab on the build report page.  Because I used NCover to run the unit tests, and didn't want to run them twice (once with the TC NUnit task, which gives you a very basic test report in TC, and once inside your build script with NCover to generate the coverage reports), I ultimately used the CommandLine build runner.  This meant I only had to run my tests once, but also manually transform the xml test reports into a html based report and register it with TC.  This is a bit more work than the basic CC.NET setup.  It does end up being prettier though.  That said you could do exactly the same with CC.NET.

The process of transforming the NUnit xml test reports into a xhtml form involved using the MSBuild XSLT task.  You simply need a .xsl transformation file (NUnitReport.xsl), which you'll find the the source distribution of the MSBuild Community Tasks, not the binary (.msi) distribution, for some reason.  The report generated is clean and fits well with TC.

Here is the MSBuild script I created which builds the project proper, and then merges the test and coverage reports, spitting out xhtml, ready for TC:

   1: <Project ToolsVersion="3.5"
   2:          DefaultTargets="Build"
   3:          xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   4:  
   5:   <PropertyGroup>
   6:     <ApplicationName>Utopia</ApplicationName>
   7:     <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
   8:     <RunTests>True</RunTests>
   9:   </PropertyGroup>
  10:  
  11:   <!-- Build the project and Run all Tests/Coverage -->
  12:  
  13:   <Target Name="Build">
  14:     <MSBuild Projects="utopia.sln"
  15:              Properties="Configuration=$(Configuration);RunTests=$(RunTests)"/>
  16:   </Target>
  17:  
  18:   <!-- Collect all Coverage report xml and build final report -->
  19:  
  20:   <PropertyGroup>
  21:     <NCoverExplorerPath>$(MSBuildStartupDirectory)\3rdParty\Externals\NCoverExplorer\</NCoverExplorerPath>
  22:     <NCoverExplorerExtrasPath>$(MSBuildStartupDirectory)\3rdParty\Externals\NCoverExplorer.Extras\</NCoverExplorerExtrasPath>
  23:     <MSBuildCommunityTasksPath>$(MSBuildStartupDirectory)\3rdParty\Externals\MSBuild.Community.Tasks\Build</MSBuildCommunityTasksPath>
  24:   </PropertyGroup>
  25:  
  26:   <UsingTask TaskName="NCoverExplorer.MSBuildTasks.NCoverExplorer"
  27:              AssemblyFile="$(NCoverExplorerExtrasPath)bin\NCoverExplorer.MSBuildTasks.dll"/>
  28:  
  29:   <Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets" />
  30:  
  31:   <ItemGroup>
  32:     <nunitReportXslFile Include="$(MSBuildCommunityTasksPath)\NUnitReport.xsl">
  33:       <project>$(ApplicationName)</project>
  34:       <configuration>$(Configuration)</configuration>
  35:       <msbuildFilename>$(MSBuildProjectFullPath)</msbuildFilename>
  36:       <msbuildBinpath>$(MSBuildBinPath)</msbuildBinpath>
  37:       <xslFile>$(MSBuildCommunityTasksPath)\NUnitReport.xsl</xslFile>
  38:     </nunitReportXslFile>
  39:   </ItemGroup>
  40:  
  41:   <Target Name="Coverage"
  42:           DependsOnTargets="Build">
  43:     <Message Text="##teamcity[progressStart 'Merging Coverage on $(ApplicationName).']"/>
  44:  
  45:     <!-- Use NCoverExplorer to merge all the coverage reports and build a Html page -->
  46:  
  47:     <NCoverExplorer
  48:       CoverageFiles="$(MSBuildStartupDirectory)\CoverageReports\*.Coverage.xml"
  49:       FailMinimum="false"
  50:       HtmlReportName="index.html"
  51:       OutputDir="$(MSBuildStartupDirectory)\CoverageReports"
  52:       ProjectName="$(ApplicationName)"
  53:       SatisfactoryCoverage="60"
  54:       ReportType="ModuleClassFunctionSummary"
  55:       Exclusions="Assembly=*.Tests;Namespace=*.Tests*"
  56:       ToolPath="$(NCoverExplorerPath)"/>
  57:     <Zip Files="$(MSBuildStartupDirectory)\CoverageReports\index.html"
  58:          WorkingDirectory="$(MSBuildStartupDirectory)\CoverageReports"
  59:          ZipFileName="$(MSBuildStartupDirectory)\coverage.zip" />
  60:  
  61:     <!-- Use the NUnitReport.xsl to transform the test reports into a Html page -->
  62:  
  63:     <CreateItem Include="TestReports\*.xml">
  64:       <Output TaskParameter="Include"
  65:               ItemName="TestReportXMLFiless"/>
  66:     </CreateItem>
  67:     <Xslt Inputs="@(TestReportXMLFiless)"
  68:           RootTag="mergedroot"
  69:           Xsl="@(nunitReportXslFile)"
  70:           Output="TestReports\index.html" />
  71:     <Zip Files="$(MSBuildStartupDirectory)\TestReports\index.html"
  72:          WorkingDirectory="$(MSBuildStartupDirectory)\TestReports"
  73:          ZipFileName="$(MSBuildStartupDirectory)\tests.zip" />
  74:  
  75:     <Message Text="##teamcity[progressFinish 'Merging Coverage on $(ApplicationName).']"/>
  76:   </Target>
  77:  
  78: </Project>

Quirks

I'm running Windows Vista as my development platform, and I have 2 bare minimum WinXP Pro VMs set up specifically for these CI evaluations.  The VMs have just enough installed to allow the build to happen, and nothing extra - no vs2008 for example.  I installed CC.NET and TC each on it's own VM with CC.NET running on IIS and TC running on Apache/tomcat.  Initially my TC setup wouldn't trigger on a svn commit, despite being told to.  I eventually tracked the problem down.  Although the time was identical on my Vista machine (where svn lives) and my XP VM (where TC lives) the time zone wasn't.  My VM was running in the future relative to my Vista machine, and it threw off the TC build trigger.  As soon as I corrected that, the triggers worked normally. This problem never presented in CC.NET despite also having the same time zone difference.  Both systems seem to use different ways of tracking changes in svn.

Conclusion

With respect to .NET development and the basic features considered above, there isn't a huge amount of substantial difference between the two, aside from cosmetics.  What TeamCity gained in ease of setup, it quickly lost in it's lack of out-of-the-box reporting (at least in the .NET scenario I ended up with).  CC.NET has a slightly more involved setup phase, but a simple and powerful reporting mechanism, if the default .xsl transformations are acceptable to you. Aside from kicking off an automatic build of your project, the meat of these packages is the reporting, and the presentation of that information is important.  Overall, CC.NET and it's reports look really dated.  You can of course create your own .xsl transformation files to compensate for this.  TeamCity looks fresher, but (depending on the Build Runner you choose) doesn't provide good, out-of-the-box reporting, forcing you to do the heavy lifting generating reports that it can display. Unlike CC.NET, TeamCity is a commercial piece of software and while you are free to use it on small projects without any fee, you will have to pay for it if you scale up.  See the JetBrains web site for details.  To be fair to TC, my evaluation is exclusively on the .NET side, and it looks to me that .NET isn't entirely a first class citizen in TC just yet, although I think it's definitely heading that way. TC still seems slightly more oriented towards Java projects, particularly with respect to code coverage. 

TeamCity does have many other bells and whistles that I haven't yet tried, like a vs2008 IDE plugin. 

Ultimately, If you're working in a team environment, having a CI server is more important than which CI server you choose.  That said, TeamCity feels like a modern piece of software.  It has a sense of polish that CC.NET doesn't have.  There's still a place for CC.NET but, for me at least, TeamCity comes out on top.

Review: Don't Believe Everything You Think

by Kevin Doolan 9. September 2008 20:54

About two years ago I came across a book that piqued my interest.  It was Don't Believe Everything You Think by Thomas Kida, and I promptly bought a copy.  I'm giving it a second reading right now.  Boy, am I glad I bought this book.

Most of us pass through the bowels of our education systems without a single course on critical thinking.  This is pretty dire, considering how fundamental decision making is to every aspect of our lives, both personal and professional.  It wouldn't be so bad if our brains were naturally deterministic, objective and reliable when forming beliefs, but they're not.  As Kida shows all too clearly, we have faulty thinking.  Our thinking naturally operates heuristically, which generally serves us extremely well.  Every now and then however, things don't go so well, and these heuristics can lead us to make incredibly bad decisions and form totally unsubstantiated beliefs.  This doesn't just apply to the usual suspects like ghosts, alien abductions or fortune telling.  Consider how much credence is currently given to intelligent design or homeopathy.  All of these subjects have a very large and devoted group of believers, including extremely well educated, intelligent people.  Kida details the six basic mistakes we make when thinking, that allow us to believe in these things.

In great detail and an easy to digest style, Kida fleshes out this Six Pack of mistakes vividly and entertainingly with plenty of meat and examples.  It's pretty compelling reading.  Eventually however, you can't help but be shocked at how seriously damaging these mistakes can be.  All the more reason to become familiar with them.

Aside from the not so threatening artifacts of the six pack above, Kida also describes cases of obscene exploitation, gross miscarriages of justice, blatant incompetence of governments, and many other horror stories, all of which are allowed to occur in our societies today because of the window afforded them by the six pack.

One of the strongest impressions I was left with was how powerful simple statistics can be in helping to make a decision, and how most of us completely fail to exploit that fact. Even when we try, we still tend to get it wrong because we don't have a good enough understanding of basic probability.  Another is our unwarranted use of anecdotal evidence in making decisions.  Again, we prefer to hear a story about something than rely on cold, hard, and ultimately more reliable statistics.  Then there's false memories, framing, and the weight given to eye witness testimony in our courts.  These are just the tip of the iceberg!

This is one of the most important books I've read recently.  It all sounds pretty obvious once you've had time to digest it, but it is unquestionably valuable.  Understanding this material points you directly towards more objective thought, and will make you think twice when evaluating information on which to make a decision or form a belief, particularly if it's serious.  It applies to everyone, we all make these mistakes - it doesn't matter what you do, or how intelligent or educated you are. 

But don't just believe me...

Writing a LensPanel (aka FishEyePanel) in Silverlight

by Kevin Doolan 27. August 2008 04:49

Writing a custom control is a pretty good exercise if you're new to Silverlight or WPF.  I originally wrote the LensPanel Flickr control for this blog in WPF and later ported it to Silverlight.  In the process I delved into Storyboards (when to use them and more importantly when not to), Dependency Properties, layout, transforms, resources and everything in between.  I recently refactored it with my brother in an international pairing exercise (he's in Ireland, I'm in Germany).  It was slightly different from the traditional XP pairing process, but extremely effective none the less - we covered a lot of ground in a very short time.  We both ended up with cosmetically different Controls (we weren't operating off the same source, just the same problem, with the same solutions applied).

Storyboards

Storyboards are used in two places, the fade up effect when an image loads (non-interactive), and for global animation when performing the manual calculations of size and position of the items based on the mouse position (also non-interactive, at least not in the traditional sense).  The latter is the 'chained' Storyboard technique recommended for Game-type applications.  Essentially it gives you a run loop.

Layout

Initially, I calculated the scale of each item and I let the base Panel class perform the layout pass, but this turns out to be a bad idea.  What happens is the items to the left and right of the lens wobble slightly as you move across the control because of width changes in the items that are scaling under the influence of the lens.  There's also an issue of centering the items - you don't always want to, like when the mouse is on the far left or right of the item group. It depends on how much you're willing to accept in terms of visible artifacts.  For me, the artifacts were too jarring so I went in and calculated layout manually.

ItemsControl, Templates and ItemsSource

The ItemsControl is the glue that holds everything together, and takes care of injecting the control with items harvested from a custom provider, FlickrPhotoProvider, which is derived from ObservableCollection.  It also allows specification of templates for the Panel itself and the Items that populate it.   Providers are ideal for a control that gets populated based on, in this case, a dialogue with Flickr's API.  Here's what the xaml looks like:

   1: <UserControl x:Class="LensPanel.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:local="clr-namespace:LensPanel;assembly=LensPanel"
   5:     Height="135">
   6:     <UserControl.Resources>
   7:         <local:FlickrPhotoProvider x:Key="FlickrPhotoProvider" NumberOfItems="12" />
   8:     </UserControl.Resources>
   9:     <Grid>
  10:         <ItemsControl x:Name="icLensPanel" ItemsSource="{StaticResource FlickrPhotoProvider}">
  11:             <ItemsControl.ItemsPanel>
  12:                 <ItemsPanelTemplate>
  13:                     <local:LensPanel ElementWidth="50" ElementHeight="100" ElementRenderTransformOriginX="0" ElementRenderTransformOriginY=".5" ElementSelectableHeightFactor=".5">
  14:                         <local:LensPanel.Background>
  15:                             <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
  16:                                 <GradientStop Color="#FF333333" Offset=".54"/>
  17:                                 <GradientStop Color="#FF383838" Offset=".68"/>
  18:                                 <GradientStop Color="#FF303030" Offset=".70"/>
  19:                                 <GradientStop Color="#FF333333" Offset=".90"/>
  20:                             </LinearGradientBrush>
  21:                         </local:LensPanel.Background>
  22:                     </local:LensPanel>
  23:                 </ItemsPanelTemplate>
  24:             </ItemsControl.ItemsPanel>
  25:             <ItemsControl.ItemTemplate>
  26:                 <DataTemplate>
  27:                     <local:LensPanelReflectedItem ImageUri="{Binding ImageFarmUrl}" NavigateUri="{Binding ImagePageUrl}" />
  28:                 </DataTemplate>
  29:             </ItemsControl.ItemTemplate>
  30:         </ItemsControl>
  31:         <TextBlock x:Name="uxError" Text="" Foreground="Silver" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
  32:     </Grid>
  33: </UserControl>

This gives you a clean separation of interests and dependency injection.  The LensPanel and LensPanelRelfectedItem have no idea about any of the Flickr code, and vice versa.

Quick and Dirty Reflections

Silverlight doesn't have a VisualBrush.  This makes typical effects a little more challenging in Silverlight, but not impossible. 

There are two options.  You can either attempt to procedurally create the reflections in code by cloning and flipping the elements, which is a lot of relatively heavy lifting on your part, or you can take the quick and dirty option and flesh it out in pure xaml. 

I opted for the xaml approach as a quick way to prototype the method, but it worked so well, I left is as it is.

Each item in the LensPanel is a Grid with two rows.  The Grid houses the Image and its' reflection underneath.  In fact the Grid houses two Grid container objects, each of which contains an image.  The reason for the Grid container is to provide an initial background, that the images can be faded onto when they load.

Here's a snapshot of the xaml for the LensPanelReflectedItem.

   1: <UserControl x:Class="LensPanel.LensPanelReflectedItem"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:local="clr-namespace:LensPanel;assembly=LensPanel">
   5:     <UserControl.Resources>
   6:         <Style x:Key="SubjectImageStyle" TargetType="Image">
   7:             <Setter Property="VerticalAlignment" Value="Center" />
   8:             <Setter Property="HorizontalAlignment" Value="Center" />
   9:             <Setter Property="Stretch" Value="UniformToFill" />
  10:             <Setter Property="Opacity" Value="0" />
  11:         </Style>
  12:         <Style x:Key="SubjectImageGridStyle" TargetType="Grid">
  13:             <Setter Property="Background" Value="#515151" />
  14:         </Style>
  15:         <Style x:Key="ReflectionImageGridStyle" TargetType="Grid">
  16:             <Setter Property="Background" Value="#515151" />
  17:             <Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
  18:             <Setter Property="RenderTransform">
  19:                 <Setter.Value>
  20:                     <TransformGroup>
  21:                         <ScaleTransform ScaleY="-1"/>
  22:                     </TransformGroup>
  23:                 </Setter.Value>
  24:             </Setter>
  25:             <Setter Property="OpacityMask">
  26:                 <Setter.Value>
  27:                     <LinearGradientBrush EndPoint="0,0" StartPoint="0,1">
  28:                         <LinearGradientBrush.GradientStops>
  29:                             <GradientStop Color="#55FFFFFF" Offset="0"/>
  30:                             <GradientStop Color="#00FFFFFF" Offset=".5"/>
  31:                         </LinearGradientBrush.GradientStops>
  32:                     </LinearGradientBrush>
  33:                 </Setter.Value>
  34:             </Setter>
  35:         </Style>
  36:         <Storyboard x:Name="fadeUp" BeginTime="00:00:00.00" Duration="00:00:01.00" >
  37:             <DoubleAnimation Storyboard.TargetName="imgSubjectItem" Storyboard.TargetProperty="Opacity" To="1"/>
  38:             <DoubleAnimation Storyboard.TargetName="imgReflectionItem" Storyboard.TargetProperty="Opacity" To="1"/>
  39:         </Storyboard>
  40:     </UserControl.Resources>
  41:     <Grid Margin="1" >
  42:         <Grid.RowDefinitions>
  43:             <RowDefinition Height="*"/>
  44:             <RowDefinition Height="*"/>
  45:         </Grid.RowDefinitions>
  46:         <Grid Style="{StaticResource SubjectImageGridStyle}" MouseLeftButtonUp="LensPanelReflectedItem_MouseLeftButtonUp">
  47:             <Image x:Name="imgSubjectItem" Style="{StaticResource SubjectImageStyle}"/>
  48:         </Grid>
  49:         <Grid Grid.Row="1" Style="{StaticResource ReflectionImageGridStyle}">
  50:             <Image x:Name="imgReflectionItem" Style="{StaticResource SubjectImageStyle}"/>
  51:         </Grid>
  52:     </Grid>
  53: </UserControl>

The upper Grid is pretty self explanatory, and holds the main image.  The reflection Grid holds the same image, and has a render transform applied to flip it.  The reflection Grid also has an Opacity Map which effectively fades the reflection out.  At no point are the dimensions of the Item specified - it's up to the LensPanel Control to set the dimensions.

There's a little duplication in there.  Right now, Silverlight doesn't support BasedOn styles, so there's not a huge amount you can do about it.

You can see the finished product on the top of this page, provided you're viewing on a system that Silverlight can run on.  If the page detects it's on a non-Silverlight platform, it collapses the Silverlight HTML object, so you don't see the "Install Silverlight" button.

Coolio...

by Kevin Doolan 18. August 2008 07:27

After playing around in inter-web-land for a few weeks I've finally set up my blog.

It's built on BlogEngine.NET with a custom theme I created based on the MS Expression Blend IDE.  I also wrote a Silverlight Flickr control to decorate the masthead.

No problems with Silverlight/.NET - everything was plain sailing.  HTML/CSS/Javascript gave me some hassle or rather the browser incompatibilities did (it's very similar to working in J2ME/BREW!)

I'm very happy with the direction Silverlight is taking.  It's not there yet, but it's certainly moving in the right direction.   There are a few gapping holes, like no proper 3D support, or any mobile support, but it's just a matter of time... 

Tags:

About

I'm a software engineer with a background in game engine development as well as art and animation.  More detail...

Calendar

<<  February 2010  >>
MoTuWeThFrSaSu
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

View posts in large calendar

Details

Powered by BlogEngine.NET 1.4.5.0
Theme and LensPanel/Flickr Control by Kevin Doolan



Hosted at DiscountAsp.net

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

This website is not responsible for externally hosted material, including linked articles, photos or any other media.

© Copyright 2009, Kevin Doolan.