On this page

XNA MVP once again in 2009
For vs Foreach Performance
Making Lua run on the Xbox 360 and PS3 (native code)
Seattle - MVP Summit 2009 - Day 7 - Getting back to Germany
Seattle - MVP Summit 2009 - Day 6 - Last day of the conference
Seattle - MVP Summit 2009 - Day 5 - Top Secret
Seattle - MVP Summit 2009 - Day 4 - At the Microsoft Campus
Flying to Seattle for the MVP Summit once again
CR_Commenter v1.9 - Autogenerate comments with this CodeRush plugin
Coolest PDC 2008 Videos
Ziggyware Fall 2008 XNA Article Contest - Win an Xbox 360 Elite and More
TV Interview with me on 3sat this Sunday 16:30
XNA Game Studio 3.0 CTP is out!
MVP Summit 2008 in Seattle USA - Thursday (Day 6)
MVP Summit 2008 in Seattle USA - Wednesday (Day 5)
MVP Summit 2008 in Seattle USA - Tuesday (Day 4)
Using Nullsofts Installer System (NSIS) to create Setups for XNA Games
Loading Collada Models in XNA 2.0 and doing Skinning and Animation
How to export Collada (.dae) files from 3DS Max for use in XNA
How to export .X files from 3DS Max for use in XNA
All XNA Games converted to XNA 2.0 from Rocket Commander to the Racing Game.
Converting XNA 1.0 Projects to XNA 2.0
What to do if you receive the System.Runtime. InteropServices.COMException error in Visual Studio
XNA 2.0 released
XNA Game Studio 2.0 beta is now available
Lots of Information about XNA 2.0
CoDe Magazine: Microsoft XNA: Ready for Prime Time?
Dungeon Quest Updated with Source Code now
Xna Project Changer Tool (with VS 2008 support)
Article in Wired about Dungeon Quest from the GDC 2007
Silverlight and I spotted A Racing Game Mod: Cyber Car
My XNA book is out and XnaProjects.Net launches
XnaRacingGame.com goes online
XNA Racing Game downloads now available on creators.xna.com
The Dungeon Quest Game
Rest of GDC Pics
After the GDC
GDC Day 4 - The Game is done
GDC 2007 - Dungeon Quest - Day 3
Dungeon Quest - Day 2
GDC 2007 - Dungeon Quest - Day 1
The GDC (Game Developers Conference) 2007 in San Francisco
Skeletal Bone Animation and Skinning with Collada Models in XNA
Welcome to my new blog
XNA News
XNA Shooter Game
Your Christmas Present: Rocket Commander XNA
XNA Game Studio 1.0 released
That leaked XNA Racer Video on YouTube
Bye Seattle, Back to Germany!
Seattle Day 5 - Another rainy day
Seattle Day 4 - From a tourist point of view
Seattle Day 3 - XNA Presentations and Meeting ZMan and Jason Olson
Seattle Day 2 - Second day at Microsoft
Seattle Day 1 and XNA Video and Canyon Commander
Seattle Day 0
Flying to the USA to visit the XNA Team
XNA Game Studio Express Beta 2 released
Quick Tips for XNA Beta 1 (aka My Own XNA FAQ)
XNA Game Studio Express Beta1 Released

MVP

Microsoft MVP (since 2006) in the XNA/DirectX category

Tag cloud

Ajax (8) All (266) Arena Wars (21) Boo (4) BroodWar (10) Conferences (19) dasBlog (2) Development (77) DLR (6) Fun (25) Game Development (164) iPhone (5) IronPython (8) Lost Squadron (17) Lua (10) meinSport.de (4) Other (196) Polynapping (12) Programming (181) Racing Game (11) Reviews (126) Rocket Commander (50) Silverlight (14) SQL (2) StudiHelp.de (2) XNA (60)

Categories

Navigation

Archive

Popular

My Bookmarks
Contact
Migrating ASP.NET VS20...
CR_Commenter Update v1...
A new more effective k...
New Fancy Guide for In...
How to write a CodeRus...
NormalMapCompressor v1...
The year 2005 - Review...
German developer price...
Raid explained.
Quick Tip: Getting rid...

Blogroll

Projects

Arena Wars (2004)

Rocket Commander (2006)

Pizza Commander (2006)

Rocket Racer (2006)

Coop Commander (2006)

Flower Commander (2006)

Fruit Commander (2006)

Euro Vernichter (2003)

Lost Squadron (2005)

Zombie Quest (very simple 2D Adventure, 2006)

Freifunk Hannover project (GoogleMaps support)

Older projects (2000 and earlier)

MeinSport.de - German Sport Community Site

About

About me: Contact

Send mail to the author(s) Email:

Total Posts: 276
This Year: 43
This Month: 1
This Week: 1
Comments: 457
Made with

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

RSS 2.0 | Atom 1.0 | CDF

Sign In

 Wednesday, July 01, 2009
Wednesday, July 01, 2009 4:53:29 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Other | Racing Game | Reviews | XNA )
Buyaa, I'm Microsoft MVP once again in the XNA/DirectX category for one more year (2009/2010). I have been an XNA MVP since 2006 and I'm still very proud of it :)

I was pretty busy this year with our current game project "Fireburst" at exDream (a racing game for Xbox 360, PS3 and PC using the Unreal3 engine, more about it soon) and theirfore I did not do many other things (except writing some iPhone games, starting to develop my own dynamic language and some tools). But once that project is done in 1-2 months, I will do lots more XNA fun stuff and hopefully XNA Community Games (now called Indie Games on the Xbox 360, I really hate that term) will be available in Germany so I can finally review and submit some games myself. I'm still pretty fit in XNA 3.1 and DirectX 11 (played around with it a lot in March), but for XNA I'm still waiting for availablilty in Germany and for DirectX 11 I'm really waiting for some cool hardware at the end of this year.

At exDream we recently also had some interesting discussions about using .NET for PS3 (recently possible thanks to Novell), Xbox 360 (hello XNA) and PC or even more dynamic script languages for upcoming projects. Maybe also including some other promising platforms such as iPhone (also .NET able thanks to Unity), Android, PSP Go, WII, whatever, but that all should depend more on the game and if we are able to manage so many platforms. Working with XNA was almost no extra work to make a game run on the Xbox 360 plus the PC, the game just has to fit and you obviously should allow control with the Xbox 360 controller. Our multiplatform game Fireburst is also not that bad to develop for because most issues are handled by the great Unreal3 engine (except the fact of course that it is all ugly C++ and UnrealScript code, which just looks like C++ anyway and even has to be compiled), but it is still quite a lot more work than just doing a PC only game, especially because of optimizations and testing required for all platforms.

Enough rambling, I'm going to celebrate this day by installing Windows 2008 server (omg) because our pre-release version we had on there just ran out (warg) .. stupid thing has to be completely reinstalled, no upgrade option ..

Comments [2] | | # 
 Wednesday, April 22, 2009
Wednesday, April 22, 2009 3:31:35 AM (GMT Standard Time, UTC+00:00) ( All | Development | Fun | Game Development | Other | Programming | Reviews | XNA )
Today I had a discussion with a few other programmers (Hi ViperDK and Timo ^^) about foreach and for loop performance. Everyone had his own ideas and experiences, but we talked mostly about XNA on the Xbox 360 (using the compact framework, that does not like foreach, just use for all the time in tight render loops for your XNA games, foreach will create too much IEnumerator garbage that will not be collected) or other special cases (huge arrays or lists).

Anyway, just guessing around is obviously not as good as actually writing a performance test application and see whats going on (see end of this article for download link). Again, we need a disclaimer: You will probably never write loops with 1 billion iterations, these numbers are similar for less iterations, but you should always try to write clean code and ONLY optimize when it is required after finding performance issues! Lets go directly to the results for 1 billion for or foreach iterations in release mode (will loop through a 10k array or list for 100k times, see below for code):



As you can see there are some major differences between using foreach with an array or using a generic list (boxing/unboxing is not the issue here, you would not be able to archive this performance with an ArrayList). Another thing you can notice right away is that using simple arrays is always faster than using dynamic lists, even converting them from a list to an array via .ToArray() and then executing the foreach is much faster.

It also makes a HUGE difference executing this in debug mode (I execute my code almost 99% in debug mode), everything is 4 times slower for this special performance test. Except for Foreach+List, which is in comparison not that bad in debug mode. What is really bad is For+List, which almost takes 16 seconds to execute 1 billion times (again using my Core 2 Duo 6600 CPU with 4 GB Ram).



Thanks to some helper methods the code for this performance test is pretty short and easy (check end of this article for the source code download link):
 Stopwatch timer = new Stopwatch();
 // Run through all tests
 for (int test = 0; test < 8; test++)
 {
     // Find out which test we wanna make
     bool rememberLength = (test / 4) % 2 == 1;
     bool useForeach = (test / 2) % 2 == 1;
     bool useList = test % 2 == 1;

     // Start timer for test 
     timer.Start();

     // Execute
     int result = RunForOrForeachLoops(rememberLength, useForeach, useList);

     // Stop timer
     timer.Stop();

     // Skip this test if we got no result!
     if (result == 0)
         continue;

     // Report results!
     resultText = ...
     Console.WriteLine(resultText + ": " + timer.ElapsedMilliseconds + "ms");

     // Reset timer for next run
     timer.Reset();
 } // for
The interesting code obviously happens in RunForOrForeachLoops. It basically executes the 1 billion iterations in several different ways (8 ways, as you can guess by the for main test for loop). All of those produce the same result, half of the tests use int[] intArray, the other half uses List<int> intList. And again half of the tests uses just for loops, the other half uses foreach for the iterations. Lets examine the useForeach=true and rememberLength=false cases (btw: Iterations is 100000 and the intList and intArrays have a size of 10000 elements):

if (useForeach)
{
    if (useList)
    {
        for (int iter = 0; iter < Iterations; iter++)
        {
            result = 0;
            foreach (int val in intList)
                result += val;
        } // for
    } // if
    else
    {
        for (int iter = 0; iter < Iterations; iter++)
        {
            result = 0;
            foreach (int val in intArray)
                result += val;
        } // for
    } // else
} // if
else // useForeach == false
...

To figure out the differences between those few lines of code in RunForOrForeachLoops, that almost look the same, we have go to IL code once again. First lets check out what the fastest code block Foreach+Array is being compiled to in IL:
result = 0;
foreach (int val in intArray)
    result += val;
This becomes in IL:
 L_0007: ldsfld int32[] TestSimpleForeachApp.Program::intArray
 L_000c: stloc.2 
 L_000d: ldc.i4.0 
 L_000e: stloc.3 
 L_000f: br.s L_001d
 L_0011: ldloc.2 
 L_0012: ldloc.3 
 L_0013: ldelem.i4 
 L_0014: stloc.1 
 L_0015: ldloc.0 
 L_0016: ldloc.1 
 L_0017: add 
 L_0018: stloc.0 
 L_0019: ldloc.3 
 L_001a: ldc.i4.1 
 L_001b: add 
 L_001c: stloc.3 
 L_001d: ldloc.3 
 L_001e: ldloc.2 
 L_001f: ldlen 
 L_0020: conv.i4 
 L_0021: blt.s L_0011
In L_0021 we jump up to L_0011 as long as we are not done with the loop yet. Inside all the result += val happens, but we do not see any slow code, there are no calls to any methods, there is no unboxing and this all can probably be executed quickly after being complied to assembler by the JIT (just-in-time) compiler.

On the other hand, if we look at pretty much the same code, just with intList instead of intArray, the whole story changes:
result = 0;
foreach (int val in intList)
    result += val;
becomes much more complex IL code with lots of function calls, the creation of an Enumerator and there is even a dispose at the end (hello GC):
  L_0007: ldsfld class [mscorlib]System.Collections.Generic.List`1<int32> TestSimpleForeachApp.Program::intList
  L_000c: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0>
[mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator() L_0011: stloc.2 L_0012: br.s L_0020 L_0014: ldloca.s CS$5$0000 L_0016: call instance !0 [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current() L_001b: stloc.1 L_001c: ldloc.0 L_001d: ldloc.1 L_001e: add L_001f: stloc.0 L_0020: ldloca.s CS$5$0000 L_0022: call instance bool [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext() L_0027: brtrue.s L_0014 L_0029: leave.s L_0039 L_002b: ldloca.s CS$5$0000 L_002d: constrained [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> L_0033: callvirt instance void [mscorlib]System.IDisposable::Dispose() L_0038: endfinally
What even looks worst too me is the code that is executed in MoveNext (get_Current just returns this.current, no extra instructions here). Since the IL code is very long and confusing (31 IL instructions), here is the C# code of MoveNext:
public bool MoveNext()
{
  List<T> list = this.list;
  if ((this.version == list._version) && (this.index < list._size))
  {
    this.current = list._items[this.index];
    this.index++;
    return true;
  }
  return this.MoveNextRare();
}

And MoveNextRare could also be called at the end of MoveNext if something changes for the list (not the case in our example). But we have to keep in mind that this is just IL code, not actual code that will be executed on the CPU. The JIT compiler can still optimize things out, which we won't see here. But something must be going on since Foreach+List is definiately slower than using all the other approaches to get through all the values in intList. I recommend Foreach+Array converted from List because the IL for Foreach+Array is really fast and does not use any extra methods or Enumerators, especially if you know that the array won't change much and if you do not have to call .ToArray every time when you do a foreach.

And here is the sample project for all this performance testing fun:

Comments [1] | | # 
 Saturday, March 28, 2009
Saturday, March 28, 2009 7:42:30 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | iPhone | IronPython | Lua | Other | Programming | Reviews | XNA )
I have been working quite a bit in Python and Lua last year and I also tried to use those languages in our current game project at exDream (will be announced shortly, so I'm finally allowed to talk about it ^^). While both Python and Lua are written in C (there are obviously other ports like the great IronPython project for .NET, but I'm not going to talk about that in this post), it was not easy at all to make Python run on the Xbox 360 or PS3. I was able in early testing to get some parts running on the Xbox 360, but too much was missed and commented out and the PS3 port looked like way too much work. So I decided to just use Lua, which is probably a better choice for our game anyway since I only use it to do some AI and physics code with it along with some settings. I'm not going to benefit from the Python libraries either way, they are not needed for our game and they won't run on those consoles without serious effort anyway.

Ok, so I was writing some Lua code and used mostly LuaInterface to make the Lua code run from .NET and of course to work with all my .NET code and even XNA. The second step was to make this run in our new game engine, which is written in native code (C++) since the whole engine is based on the Unreal 3 Engine. For that I used the normal Lua 5.1.4 release, which compiles just fine and works great. Since Lua is written completely in ANSI C I was able to quickly test some code on the Xbox 360 and PS3 too. This is important because our game will be a multi-platform title (PC, Xbox 360 and PS3), which is thanks to the Unreal 3 Engine a much simpler task than writing our own engine for all those platforms (it has obviously it own disadvantages, not only do we have to use native code, but we also have to work with a lot of code in the engine that we don't really need for our game).

So far so good. While it sounds like a simple task to make Lua run on all platforms, there is a lot of pain involved to make it run perfectly and behave the same way on all platforms. Since the Unreal 3 Engine is written in C++ and uses its own scripting Language UnrealScript, it does not help out a lot when you want to integrate your own code or libraries in there, so this was half of the work (just see my previous post about compile times and you see why this is a much lengthier process than writing some .NET code). Secondly there are many special rules on both the Xbox 360 platform and the PlayStation 3, some ANSI C methods are just missing or just don't do anything because of the platform design. For example on the Xbox 360 you can't use relative paths, you always have to specify a full path when opening a file, which is something that the Lua engine does not do out of the box. After figuring out where to store the Lua scripts on the consoles and how to load them, everything else just falls into place and works finally as expected.

To make things simpler for other people trying to run Lua code on the Xbox 360 or PS3 I wrote the following little solution:
As you can see on the right the solution consists of 4 projects:
  • LuaSupport, which is based on the current Lua 5.1.4 release from 22 Aug 2008 with my own additions to support the XBOX and PS3 platforms (uses XBOX and PS3 defines).

  • TestLuaPC is a simple test project for the PC to make sure everything still runs as expected on the PC. Please note that on the PC Lua assumes that all files can be found in the current directory. I just change to the Lua script directory and then execute the Lua files there normally.

  • TestLuaPS3 will probably only compile if you have the PS3 SDK installed, just unload this project if you don't have it installed yet. Also please test other SDK samples first to make sure everything is correctly setup (it uses a lot of extra include paths). In case you are not able to open the files, check for yourself if the /app_home/Lua/ directory contains the required files after building the project (use the ProDG Target Manager for that). Debugging can also be complicated, I guess it does not work on Windows 7 at all, but I was able to debug and run the app on a normal Windows Vista PC. In any case you can also just run the .elf file from the ProDG Target Manager and then see the output in the console.

  • TestLuaXbox360 is the project to test Lua on the Xbox 360. You will also need the XDK (Xbox Development Kit SDK) installed and have a Xbox 360 DevKit in your network for testing. A TestKit also works, but you won't be able to debug, you can just run the app and check the console output yourself.

Some of the Lua functionality is not available on the Xbox 360 or PS3 like lua_popen or lua_pclose (among others), and there is also some code that will not do anything like os_execute, os_getenv and some other os methods because they won't make sense on a console anyway or could not be implemented. I did not use these methods anyway, but if you need those and want to write your own code for them, go ahead.

Lets focus on the 2 most important code changes. First of all the Lua default directories had to be adjusted in luaconf.h:83 to support Xbox 360, PS3, PC and Linux platforms:

#if defined(XBOX)
/* On the Xbox we just use the D:\ drive for everything, all lua files should be
   located there */
#define LUA_LDIR    "D:\\Lua\\"
#define LUA_CDIR    "D:\\"
#define LUA_PATH_DEFAULT  \
    LUA_LDIR"?.lua;"  LUA_LDIR"?\\init.lua;" \
    LUA_CDIR"?.lua;"  LUA_CDIR"?\\init.lua"
#define LUA_CPATH_DEFAULT \
    ".\\?.dll;"  LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"

#elif defined(PS3)
/* On the PS3 we just use the /app_home/Lua/ sub directory for everything,
   all Lua files should be located there */
#define LUA_LDIR    "/app_home/Lua/"
#define LUA_CDIR    "/app_home/"
#define LUA_PATH_DEFAULT  \
    LUA_LDIR"?.lua;"  LUA_LDIR"?\\init.lua;" \
    LUA_CDIR"?.lua;"  LUA_CDIR"?\\init.lua"
#define LUA_CPATH_DEFAULT \
    ".\\?.dll;"  LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"

#elif defined(_WIN32)
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
** path of the directory of the executable file of the current process.
** Note: We use the ..\Lua\ directory by default, but that is also the current
** directory, we change it in AIEngineInterface before running scripts!
*/
#define LUA_LDIR    "!\\..\\Lua\\"
#define LUA_CDIR    "!\\"
#define LUA_PATH_DEFAULT  \
        ".\\?.lua;"  LUA_CDIR"?.lua;"  LUA_CDIR"?\\init.lua;" \
        LUA_LDIR"?.lua;"  LUA_LDIR"?\\init.lua"
#define LUA_CPATH_DEFAULT \
    ".\\?.dll;"  LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"

#else
/* Default linux/mac directories */
#define LUA_ROOT    "/usr/local/"
#define LUA_LDIR    LUA_ROOT "share/lua/5.1/"
#define LUA_CDIR    LUA_ROOT "lib/lua/5.1/"
#define LUA_PATH_DEFAULT  \
        "./?.lua;"  LUA_LDIR"?.lua;"  LUA_LDIR"?/init.lua;" \
                    LUA_CDIR"?.lua;"  LUA_CDIR"?/init.lua"
#define LUA_CPATH_DEFAULT \
    "./?.so;"  LUA_CDIR"?.so;" LUA_CDIR"loadall.so"
#endif

And the most important code change is located in luaL_loadfile method, which is called by the lua_dofile macro. Please note that I did not change the code formating and tried to leave as much code as is. I have my own coding conventions, but it does not make sense to apply them when only changing <10% of the code of an existing library.
LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
  LoadF lf;
  int status, readstatus;
  int c;
  char fullFilename[128];
  int fnameindex = lua_gettop(L) + 1;  /* index of filename on the stack */
  lf.extraline = 0;
  if (filename == NULL) {
    lua_pushliteral(L, "=stdin");
    lf.f = stdin;
  }
  else {
    // Note: Always open as the requested file, Lua should not care about
    // our crazy directory remapping.
    lua_pushfstring(L, "@%s", filename);

#if (PS3)
    // On the PS3, check always the /app_home/Lua/ directory!
    // This works both for dev-testing via remote HDD and on the game disc.
    // These work probably too just for developing, but app_home is fine!
    // /host_root/Lua/ or /dev_bdvd/PS3_GAME/USRDIR/Lua/
    CheckForValidFilenameWithPath(fullFilename, filename, "/app_home/Lua/");
#elif XBOX
    // For the Xbox we have to make sure always to load files from D:\ because
    // fopen ALWAYS expects a full paths, there are no current directories on the
    // Xbox 360 and therefore no relative paths! Check always "D:\Lua\<file>"
    CheckForValidFilenameWithPath(fullFilename, filename, "D:\\Lua\\");
#else
    // On the PC we just use the default search logic (see luaconf.h) and we
    // don't care about directories since we will already be in the correct one!
    // In earlier versions we had a lot of extra checks here.
    strcpy(fullFilename, filename);
#endif

    // Rest of this code is untouched, we just use fullFilename now!
    lf.f = fopen(fullFilename, "r");
    if (lf.f == NULL)
      return errfile(L, "open", fnameindex);
  }
  c = getc(lf.f);
  if (c == '#') {  /* Unix exec. file? */
    lf.extraline = 1;
    while ((c = getc(lf.f)) != EOF && c != '\n') ;  /* skip first line */
    if (c == '\n') c = getc(lf.f);
  }
  if (c == LUA_SIGNATURE[0] && fullFilename) {  /* binary file? */
    lf.f = freopen(fullFilename, "rb", lf.f);  /* reopen in binary mode */
    if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
    /* skip eventual `#!...' */
   while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
    lf.extraline = 0;
  }
  ungetc(c, lf.f);
  status = lua_load(L, getF, &lf, lua_tostring(L, -1));
  readstatus = ferror(lf.f);
  if (filename) fclose(lf.f);  /* close file (even in case of errors) */
  if (readstatus) {
    lua_settop(L, fnameindex);  /* ignore results from `lua_load' */
    return errfile(L, "read", fnameindex);
  }
  lua_remove(L, fnameindex);
  return status;
}

The code in luaL_loadfile will call the new method CheckForValidFilenameWithPath, which helps us to build a full path filename for the Xbox 360 and PS3 platforms. It will just add D:\Lua\ or /app_home/Lua/ in front of the filename if it does not exist yet. Here we can see another pain point of C code, it takes so freaking long to do very simple tasks (in .NET this would be a 2-liner).

static void CheckForValidFilenameWithPath(char* fullFilename,
  const char* filename, const char* platformPath)
{
  int i = 0;
  int pathLen = (int)strlen(platformPath);
  // Already have a filename with the correct path?
  if ((int)strlen(filename) > pathLen)
    for (i = 0; i < pathLen; i++)
      if (filename[i] != platformPath[i])
        break;

  // Found path?
  if (i == pathLen)
  {
    // Then just use the existing file, happens when loading libraries from Lua
    strcpy(fullFilename, filename);
  }
  else
  {
    // Copy path
    for (i = 0; i < pathLen; i++)
      fullFilename[i] = platformPath[i];
    // Add the relative filename
    for (i = 0; i < (int)strlen(filename); i++)
      fullFilename[i+pathLen] = filename[i];
    // And finally close the string
    fullFilename[(int)strlen(filename)+pathLen] = 0;
  } // else
} // CheckForValidFilenameWithPath(fullFilename, filename, platformPath)

And that are the most important code changes to make everything run on the Xbox 360, PS3 and PC. Here is the main method for all the TestLua projects (always pretty much the same, only the Xbox 360 uses a slightly different way to print to the console):

int _tmain(int argc, _TCHAR* argv[])
{
    printf("Initialize Lua\n");

    Lua = lua_open();
    luaL_openlibs(Lua);
    lua_register(Lua, "print", lua_print);

    // Simple print test from Lua:
    luaL_dostring(Lua, "print(\"Hi from Lua\")");

    // Now load a Lua file, which also loads another Lua file (LuaUnit.lua)
    if (luaL_dofile(Lua, "SomeClass.lua") != 0)
    {
        printf("Error: Unable to execute Lua file %s: %s\n", "SomeClass.lua",
            lua_tostring(Lua, 1));
    } // if (luaL_dofile)

    // Stop the Lua engine and cleanup
    printf("Closing Lua\n");
    lua_close(Lua);
    Lua = NULL;

    // Keep command window for debug mode (ctrl+f5 does not have this issue)
    char input[100];
    gets_s(input);

    return 0;
} // _tmain


Summary:
  • Using Lua on other platforms is easier than Python or other dynamic languages I have checked because Lua is written completely in ANSI
  • Making code run on multiple platforms is still hard in C++ because you not only end up with ugly #define code, but you also have to test a lot
  • Building native applications is freaking time-intensive, it took me almost a full day to write this app and test it on every platform from serval PCs. In .NET it would take me less than an hour, but then again there is no .NET on the PS3 and our current game engine is using native code anyway.
  • On the Xbox 360 you always have to use full paths like D:\Lua\SomeClass.lua, relative paths won't work. The SDK says you should use Game:\, but since our game engine uses D:\ too, I left it that way in this sample too.
  • The PS3 is even more complicated with lots more possible mounted paths, but it seems /app_home did always work in both my samples and our game, so I'm sticking with it :)
If you are interested in Lua from .NET, check out the LuaInterface project. Someone also started NUA (Lua on the DLR) 2 years ago, but it seems this project (and many other attempts) have died a long time ago. There was also a port by the Lua guys (especially the great Fabio Mascarenhas, who has also started the LuaInterface project and is still involved) called LuaCLR, but it is still in alpha stage and pretty useless, LuaInterface on the other hand is a fully working library! The difference here is that LuaCLR emits CLR code like any other .NET language, while LuaInterface still relys on the Lua runtime. LuaInterface uses only 2 very simple C++ files, the rest is in C#, but because of that you currently cannot run it with XNA on the Xbox 360. IronPython, while fully written in C#, does not work on the Xbox 360 either because reflection is not supported by the Xbox 360 .NET framework.

And finally here is a link to Lua running on the iPhone: Lua v5.1.3 for iPhone/iPod, kinda cool, but since we can run C# code on the iPhone with help of the great Unity3D engine, I'm happy with that at the moment. The main problem for other languages on the iPhone is the missing libraries anyway (can't use Cocoa or any own UI) ..

Just in case you have not read about it yet: John Carmack has ported the good old Wolfenstein 3D game from 1992 to the iPhone as well.
Comments [1] | | # 
 Friday, March 06, 2009
Friday, March 06, 2009 10:16:27 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Fun | Other | Reviews | XNA )
While it was fun staying in Seattle for a week and meeting all the MVPs and Microsoft people again, I was happy to finally return back home. I missed my computer :D


I took some more random pics of the streets of Seattle.


It took almost 30 minutes until the bus to the airport came, but I still had 3 hours left, more than enough time. I also met up with a German Windows Mobile MVP, which was pretty much pissed about Windows Mobile 6.5, which did not has any new useful features and just can't compete with the iPhone.


As usual, for me it is always fun to take pictures of people taking pictures.


Heading to the Seattle airport.


Arriving at the check-in gates. Still 3 more hours to kill.


Oh no, self-service check-in computers. This will end horribly as usual! This crappy thing did not detect mine or that other MVPs guys passport and due to all the back and forth it takes like 3 times longer just to get the boarding pass. I also only did get the boarding pass for Seattle to Amsterdam, more on that problem later.


I think I saw this crazy thing before last year at the Seattle airport ..


Waiting in line for the security check. Always fun to have some Asian guys in front of you that did not understand the security officer very well and made everything wrong they could do (not taking off their shoes, not keeping their passports in their hands, not removing the laptops from their bags, etc.). But I have to say getting the boarding pass and going to security is taking less time every year. Some guy had a T-Shirt explaining Geeks to us, which is helpful to know ^^


This thing is strange, buy a DVD for $ 5.99 and you can only watch it for a day because the DVD has a protective film on it and once to remove it for watching the DVD will be unusable after a day or two.


After walking around in some shops, but not finding anything useful (no good books or other stuff I wanted), we could finally board the ship, erm, plane. As you can see it was raining outside, same weather conditions as in Amsterdam and Hannover.


Seattle is getting smaller and smaller ..


For most of the flight we could not see much, it was very cloudy in the beginning, then we flew through the night from Thursday to Friday and in the darkness you cannot see anything either (not much going on around the North pol area anyway).


After about 10 hours it got bright again. I could not sleep on the plane as usual. Now it is Friday and we are flying over the UK right now.


And about an hour later we finally landed in Amsterdam, where it was raining too. The airport is really huge, I'm always amazed when landing there.


A last look at the plane that got us here so far.


Now I'm quite confused. I do not have a ticket to Hannover yet and I thought I have to get my luggage and then check-in again for my boarding pass to Hannover. This happened to me few years ago, but I had a lot of time between the flights, not just a little over an hour.


So I headed over to the Luggage Drop off just to find out that all the luggage is transfered to the next flight anyway. I took a closer look at the first boarding pass and on the back there was a little sticker telling me just that. Warg, let's get back to the gates.


But security would not let me through the other way. They said I had to go through Customs, then to the Arrival area, up to the Departure area and then check-in there again, go through security and so on. OK, I did that but in order to get through security you need a boarding pass, which I obviously did not have. So I headed to the self-service check-in area to one of those stupid computers. I tried to get my boarding pass and the computer freaked out. My first attempt was aborted and then I tried it again just to get a random error and the computer telling me I had to go to the custom check-in area. Some guy came and told me to try it once again, same thing happened. Then I had to wait in line for about 25 minutes to finally get my boarding pass from the custom check-in area. But the boarding time to that plane to Hannover was just 5 minutes away and I still had to get to security and then head to that gate. I rushed over there with some girls in front of me at the security gates, which similar to the Asian guys in Seattle did everything wrong they could (not taking off their jewelry, not unpacking their bags, etc.). But I arrived just in time at my gate and got on the bus to the plane. Pooh, this is just too much stress after being awake for 18 hours ..


After about just 1 hour we arrived in Hannover with this little plane. The stewardess was a little bit annoying not letting me hear some music from my mp3 player, because that could crash the plane. Yeah, sure .. just leave me freaking alone ..


The Hannover airport is not very big, but a lot of people probably arrived here this week because of the CeBIT, which I will attend to tomorrow as well. My brother already told me about some cool things from there.


I probably have photographed this model of the Hannover airport before, but I was too tired to remember. Looks cool though.


And this was my train (S-Bahn) getting me home. I could stay away by hearing very loud music, which probably annoyed some people in the train, but I did not care at that point :)


I took a little nap and headed to the office in the evening just to test out if the US-Version of Halo Wars works with my 3 year old Xbox 360. And yes, it does. Great! Good thing most games to not use that stupid region-code anymore these days.
Halo Wars itself is nicely produced, the cut-scenes and videos are top-notch. It is probably also fun to play for Halo fans and RTS newbies. But this game is in no way comparable to any PC RTS game. It is more like playing shooter or simple role playing game from above. The controls are pretty much the same as in Lord of the Rings and C&C Red Alert 2 on the Xbox 360, which is pretty much crap and highly simplified. The game is also way too easy, I just have played the first 3-4 missions, but you spend 90% of your time to press LB (selects all units you have) and then X to attack or move. The rest of the time you go back to your base with the horrible move control and build the very simplified base and some units, which is even annoying because you can only build when you have enough money, you cannot queue anything up for later. Last but not least the graphics are not very good, after watching those very high quality cut-scenes you will be thrown back to reality with the poorly lit scenes in-game and the horrible shadow-artifacts and very low polygon models (which does not make sense since the Xbox 360 is usually fill-rate limited, not so much vertex-limited). Also I would like to mention that I really hate the fact that every single Xbox 360 game says 1080p on the box and none of them support that mode, they all just render to 720p and scale everything up, which just looks horrible! Especially if you are used to Full-HD and much higher resolutions on the PC as I am every day.
It will probably sell well anyway as all Halo games have. But do not expect this game to be released on the PC ever. It would just not work and probably require much different controls and be way too boring and easy for PC gamers. Also Essemble Studios is no more and Microsoft will probably not want to port it to PC anyway.
But it is very cool that US Xbox 360 games work just fine in Europe. NTSC/Pal issues are non-existant anyway because for HD TVs these formats are not used (and would be supported anyway).


This trip was a lot of fun and I hope you enjoyed my pictures and comments. My webserver crashed several times in the last days and was not able to handle all requests (its running on a 1mbit dsl line, which pretty much sucks). I switched to another 1mbit dsl line, which seems to be more stable (but is still slow). I will have to switch to a real server next week, this is just too much for my little server at the office. Luckily I have access to some other servers. I just was too lazy in the past to setup my blog there (and clean it up a little), but now it makes sense since I'm blogging again, yay!
Comments [1] | | # 
 Wednesday, March 04, 2009
Wednesday, March 04, 2009 8:40:24 AM (GMT Standard Time, UTC+00:00) ( All | Conferences | Fun | Other | Reviews | XNA )
The last day of the MVP Summit (Wednesday) was actually quite short, just a couple of keynotes. But since the last few days have been so packed and exciting, it was nice to have some time to finally relax. I also needed that time to finally finish blogging these posts since I lagged several days behind.

Walking over to the Convention Center was the usual 1 minute walk. Initially I was a little confused because there was no brakefast at the hotel, but then I remembered today it will be at the Convention Center.


My breakfast for today. I was kinda late because I slept relatively long, but then again we stayed at the EMP Party to the very end last evening.


Tony Richards got everything started this day too.


The theme of this MVP Summit seemed to be Windows 7 and this stupid "I'm a PC" slogan. We even got some stickers for that. Nick had one of those stickers on his Mac, but that seemed strange too.


As you can see the crowd was very excited about something. Who can guess what is happening next?


Yes, you are correct, Steve Ballmer is coming up and bringing a lot of excitement with him, as usual. Even though Steve just reiterated whats going on at Microsoft, it was fun to watch him present and talk.


The Q&A had to be messed up with stupid questions and giving gifts to Ballmer as usual. But it got even worse in the next talk, which was supposed to be about Visual Studio 2010, but I did not even see a single screenshot of it. Probably have to check out the VS2010 PDC sessions sometime soon again (when the next beta/ctp of VS2010 comes out, hopefully that one is not on a virtual PC, which was just way too slow for me to do anything).


There was a short break between the first two keynotes and the last two keynotes.


Some guy had a T-Shirt explaining Geeks to us, which is helpful to know ^^


In the next keynote we saw a video about a little girl telling us how great VS is. Seems like that Video has been on YouTube for about a year and it does not seem to have been watched much.

After some other "This is coming up" slides Somasegar introduced us to the technical panel, which even included Anders Hejlsberg. But the Q&A just messed everything up again, most people were just asking why VB6 is not supported better or why Fox Pro is not supported. Same procedure as every year. The last talk about Windows Server 2008 RC2 and Hyper-V and other cool virtualization technologies was interesting again.


And thats it, the MVP Summit 2009 is over. In usual tradition all the XNA/DirectX MVPs got together at the last lunch and we even put together 2 tables again, same as last year.
Richard Thomson (on the right) has been a DirectX MVP for a long time and usually has a lot of stories to tell.


Nick Gravelyn and Vicente Cartas Espinel (Spanish XNA MVP) talking.


Even Andy is listening in, which is a very rare sight (him not talking that is).


Catalin Zima and Andy Dunn. After about 2 hours of talking, everyone split up and left the Conference Center. Some of us also went to some side sessions.


I took a couple more pictures in the streets.


And I went to the movies once again after blogging a bit. This time I saw Taken, which is a very action packed movie, that might not much sense in regard of one guy killing 20 people per minute, but the overall story was good and the pace was also quick so it never got any boring.


And finally some pictures of the Seattle Skyscrapers.


Tomorrow morning I will head to the airport and begin my long travel back to Germany. Not really excited about getting back, but at home it is always most comfortable. At Saturday I will also attend the CeBIT 2009 in Hannover, but I will probably make a short post of tomorrow as well. I hope you enjoyed my pictures and reporting so far.
Comments [8] | | # 
 Tuesday, March 03, 2009
Tuesday, March 03, 2009 7:11:24 AM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews | XNA )

Time to get up for Day 5 (Tuesday), the second day on the Microsoft Campus with deep technical sessions.


Looking at Mt. Helen from the bridge between Seattle and Redmond.


And finally getting over to the same buildings as yesterday after arriving at the Microsoft Convention Center. I will also come back here later this day for some extra sessions.


Back at the XNA Building I decided to take a look at some cool DirectX 11 sessions after the first XNA session, which was all about XNA Community games, which is still not available in Germany. So this talk not too exiciting for me yet, but stay tuned, hopefully later this year XNA Community Games will be introduced to Germany as well.


Since all the talks (especially everything about DirectX 11) was heavily under NDA, I can't talk about any of it, neither could I take any pictures. All I can say DirectX 11 is pretty amazing and I will definately check it out once hardware and Windows 7 is available (hopefully also later this year). So to keep the pictures going I took some boring photos like this from the ceiling of the XNA/DirectX sessions building.


Later a couple of guys helped out Kathleen getting some beers up to the last session and Q&A, which was a nice oppertunity to see under a Microsoft building. Might not be terribly exciting, but at least something to remember this day by.


After the last XNA session and some beer, some guys headed over to the company store. The only thing I wanted to buy here in the US is the Intel X25-M SATA SSD hard drive, which I could not found anywhere. Buying it online is also impossible since noone is willing to ship it to europe. So I have to buy it in Germany at 135% of the price here.
At around 5 pm we went back to the Convention Center and most guys went back to their hotels, but I stayed to check out a Paralell Programming session, which was quite interesting at first. The presenter was not very excited. But it was nice to see some cool tools of Visual Studio 2010, especially for debugging multithreaded applications. The stuff he was talking about involved a raytracing application with some spheres, which took even multithreaded 9 seconds for a 200x200 pixel image, so not very convincing to me (this kind of problems are easy to split up into multiple threads) and the other topics he scratched on did not sound very exciting either and since I hoped to see more VS2010 tomorrow at the keynote, I also headed back to the hotel at around 6 pm.


It was raining quite a lot and the EMP party (here are some videos from last year) has already started, but I headed out to finally buy some new batteries for my camera.


Since it was not allowed to take photos of the Science Museum at the EMP and I just wanted to drink and talk I did not take the newly operational camera with me. Instead you can see a boring picture of my hotel room until more pictures will be done tomorrow.
Comments [1] | | # 
 Monday, March 02, 2009
Monday, March 02, 2009 5:33:27 AM (GMT Standard Time, UTC+00:00) ( All | BroodWar | Conferences | Fun | Other | Reviews | XNA )
Day 4 (Monday) was actually just the second day of the MVP Summit and the first day all MVPs went to the Microsoft Campus. I was looking forward to see all the XNA MVPs from last years again.

The bus was packed as usual and I thought I was prepared very well, which turned out to be a wrong assumtion in a few minutes. Sorry, my main camera batteries are still dead, so not many pictures today, but at least I try to mention the people on the pictures and link to them.


As you can see the weather was not very good in the morning. Typical Seattle weather, but it got much better later this day.


At the MSCC (Microsoft Convention Center) at the Campus I pulled out my schedule for the day and tried to find out where do I have to go next. But there were no freaking building or room numbers on the schedule. Apparently if you have printed it out earlier than 3 days before the summit, there was no building/room information. Wtf? I tried to go online with the iPod Touch and it took me 5 minutes just to type in the stupid password and wait for timeouts. Then I tried to find out the schedule, but no, you have to logon to the MVP Summit site first, which again required a password I had not with me. I gave up and asked someone and was told Building 85 is probably the right one for me. I took the Tour 5 bus to get over there (the Microsoft Campus is quite huge).


At the building no one seemed to know what I was talking about when I asked where the XNA/DirectX session is. So I just pulled out my laptop and had to login again to the MVP Summit site just to find out the freaking room number. At least it just took a minute to get to that information from my laptop. I just should not use a freaking mobile device to find that kind of hidden information in the future. The meeting had already started, but I did not miss much because it was still a general talk about XNA and game development in general by the XNA game platform general manager Boyd Multerer. BTW on the right in the white T-Shirt you can see Catalin Zima, which I met last year for the first time. He seems to be on the XNA Community Forums quite a lot with the other crazy Forum MVPs like Andy "ZMan" Dunn and Nick "MrTwitter" Gravelyn.


The next talk was about upcoming XNA features and you can see all of that on the screen in the middle. You probably only get that joke as an MVP with an NDA that has to keep quiet about upcoming XNA stuff ^^


After some more talks and lunch we went to some test laps to test some games and play around (probably can't tell you more about this). On the right we see Nick smiling and probably twittering as usual. Apparently he was a little annoyed about some pictures I took of him last years, where he was in the middle of telling something ^^


At the Xbox 360 Building there have a lot of cool stuff staying around and many posters on the walls. Some guy had even some cool Left4Dead posters on his door, which I now want too since I'm a huge Left4Dead fan (on the PC however). No idea where to get such things. That Halo guy is not bad either.


Later that day we had some time to kill and went to the mall and checked out this book store for some XNA books, where some MVP was involved. Andy is grinning into the camera as usual and is continuing to tell us that we should not write books ^^


We also found a board and card game shop, where people could actually and play and try out games, cool stuff. But I have not played anything like that since I'm out of school. BTW on the right you can see Vicente Cartas Espinel, which is now my most favorite XNA MVP since he asked me to give him an autograph for one of his friends ^^


And yes, there was even a StarCraft board game. Also a lot of World of Warcraft, Star Wars, Settlers, etc. But since I do not know any people that would play such things with me, it is not really interesting. Probably fun stuff.


And finally we went to the XNA Party at some stylish bar, which included billard, rock band, pizzas, beer and cocktails. Very fun evening and it was very nice to learn more about the new MVPs and speak to them.


We also got some cool extra stuff from the Xbox/XNA team including the new Halo Wars Xbox 360 game, that will come out tomorrow. I'm a huge RTS fan, but I'm still not sure this will be as great as everyone says because both C&C and Lord of the Rings pretty much sucked on the Xbox 360 for me (I think it is even worse than controlling 3d shooters, which is pretty crappy too). Anyway, probably still a very good game and I'm very excited to try it out once I'm back in Germany. Hopefully it will even run on my German Xbox because it says NTSC on the box (we got Pal in Europe), but as far as I heard most games do not check for the region code anymore and PAL/NTSC does not matter anymore with HDTVs.
Comments [0] | | # 
 Friday, February 27, 2009
Friday, February 27, 2009 1:39:12 AM (GMT Standard Time, UTC+00:00) ( All | Conferences | Development | Other | Reviews | XNA )
Yep, it is time for the MVP Summit again (starts this Sunday) and for that reason I will fly to Seattle once again in a few hours. Seems like I go there every year now ^^

This time I will have some extra time to run around and enjoy the cold weather (its not better here in Germany anyway). I will make photos as usual and have hopefully something interesting to say about whats going on.

Since I have a Zune, an iPod and a Creative Zen player and I will take all of them with me:
  • Zune for playing around with XNA
  • iPod for many apps and since I'm doing a bit of iPhones development right now
  • And the Zen for actually hearing music and watching some movies. I just uploaded a crap-load of stuff onto that thing. Really cool that each movie or tv show is just a few 100 MB.
All 3 devices have pretty cool hardware. The Zune is not used much and kinda expensive, but at least interesting for its XNA capabilities.

The iPod is just way to expensive, but interesting for iPhone development. The iPhone is kinda cheap, but you will need a very expensive phone provider contract, which is over 1000 euros over 2 years here in Germany.

And then there is the Zen (or other devices), which do not have many extra features, but its cheap and it just works. It even got a SD slot so I can extend the flash memory. But the main reason why I use the Zen all the time is the amount of hours I can hear music or watch videos on (Zune or iPods with hard disks just suck in that regard, they will be empty in half a plane ride) and because I can put whatever I like on that thing. There is no stupid Zune player or iTunes to install. It does not tell me that I cannot use any files or that I have connected an iPod on too many PCs. Just drag and drop files over, which I often use for safes or copying files from home to work, or just drag in music, add some podcasts and for the plane ride I now also got some videos on there :)

BTW: Everything with the Zen mp3 player is great, even the software you can optionally install is non-intrusive for the most part. The only little annoyance is that it takes a long time to convert videos to .wmv files with 320x240 pixel resolution (required for playback) using the Creative Video Converter. But there are some other programs that can help:
  • I tried AutoMKV, which always crashed for me when trying to do .wmvs, but other formats did work.
  • There are tons of iPhone Video converter apps out there, most cost money and almost all of them are pretty crappy (both for performance and stability).
  • And then there is the Free Zune Video Converter 1.1, which works pretty nice and is incredible fast.
  • After that I stopped searching, there is probably more out there (even converters on the web, where you upload your movies, wtf?). Creative Video Converter is quick and dirty minus the quick, Free Zune Video Converter is fast and easy and VirtualDub is for the more complex jobs ^^
Ok, time to go to bed now, plane is lifting off in a few hours ..
Comments [0] | | # 
 Thursday, November 27, 2008
Thursday, November 27, 2008 8:05:45 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | IronPython | Lua | Programming | Reviews | SQL | XNA )
CR_Commenter I have updated my CR_Commenter plugin quite a lot last weekend and tested it all week. CR_Commenter v1.9 new features include full hotkey and feature customization, much better support for C++, Lua, Python and way better light-commenting features and more powerful and stable region features. Thanks to the many people writing me emails using the addin or even improving it themselfs and sending me updated code back (full thanks are listed below).

Download links for CR_Commenter v1.9 (2008-11-27):

If you do not know about the CR_Commenter plugin for CodeRush yet (I originally wrote it back in 2004 and still use it every single day), please read the article for version 1.8 from earlier this year where I also added a lot of features and this article of version 1.7 from 2006 or check out the original Commenter - A CodeRush plugin which helps you to comment your code article on CodeProject.net from 2004, which I have finally updated too (its about time).

To use CR_Commenter you need to install CodeRush 2.0 or later, which is a commercial product you can get from DevExpress here. You can also use the free DXCore component with a reduced feature set, but all the basic CR_Commenter features should work fine there too. Please note that there is also a recently released free CodeRush Xpress Edition, which should work too, but I have not tested it.

If you have used CR_Commenter before the first thing you will probably notice is the completely new options screen:
CR_Commenter Options

Let me go through all the new and improved features. Over the years so many things were added to CR_Commenter and since the options screen, hotkeys and feature set has changed so much all the time, it is hard to keep track what features are available in the latest version. To make it easier for new users to understand what you can do with CR_Commenter v1.9 I decided to explain every feature, which might also be useful for existing users as they might have missed some of the cool features before. All these features can now be enabled or disabled and you can set your own hotkey for most of them (if it makes sense). I will go through the same list as seen in the Options screen, which is also represented in the FeatureHotkey.cs class that has a lot of comments and explanations for each feature. As mentioned before all Hotkeys can be changed to whatever you like. Please note that the pictures might look a little different than your Visual Studio code, I just use a different theme and bigger font sizes, it is still Visual Studio 2008.

CR_Commenter v1.9 Features:
  • Automatically add comments after closing blocks with }
    Hotkey: }
    This is the most basic feature of CR_Commenter. In v1.9 you can now enable or disable it as you like. Please also note that you can now specify to only generate comments for blocks greater than a certain size (by default 5), which reduces the amount of comments generated greatly and is also the new default setting! Note: Since this is only parsed after pressing } it only works for C like languages like C, C++, C#, fx, UnrealScript, etc. For some of the newly supported languages like Lua (or VB, which is still untested and more or less unsupported sadly) or even Python that use identation for blocks you have to use Ctrl+1

    CR_Commenter Commenting

  • Force Comment Block
    Hotkey: Ctrl+1
    Same as }, but allows us to comment whole classes and methods easier by positioning the text cursor on what you want to update. Use Ctrl+6 for always updating the whole file no matter where you are located. Can also be used for languages not using } for commenting or if you have disabled } commenting.

    CR_Commenter Force Commenting

  • Add namespace for the current word. (Disabled by default)
    Hotkey: Ctrl+2
    Used to support automatically adding a namespace in C# 3.0/VS 2008. Not longer needed since VS supports it on its own now! This feature could be used back in 2006 to add namespaces in VS 2005.

    CR_Commenter Add Namespace

  • Autogenerate region around the current block
    Hotkey: Ctrl+3
    Autogenerate region around the current block (method, property, constructor, etc.), for selections use Ctrl+R (default CodeRush hotkey). The most important thing about this feature is that it will automatically give your new region the method name and it will make sure all comments belonging to the method are put inside the region. Note: In the picture the #region SetNewValue and #endregion parts are hidden by CodeRush with lines to make the source code look cleaner.

    CR_Commenter Autogenerate Region

  • Autogenerate regions for the whole file (all methods)!
    Hotkey: Shift+Ctrl+3
    Same as Ctrl+3, but will go through the whole file and generate region blocks for every method that does not seem to have one yet! You can also just go to the class level and press Ctrl+3 to generate all regions for all class methods. Since v1.9 also works with C++ and will generate #prama regions for you. BTW: Parsing the whole file with Ctrl+1 will also generate a includes region automatically for you.

    CR_Commenter Generate All Regions

  • Toggle collapsing and expanding current selection
    Hotkey: Ctrl+4
    Very useful to avoid having to use the mouse for these things. Please note that Visual Studio also supports collapsing sections with Ctrl+M+M, but that will only work on sections, not on regions, which is a pretty useless feature in C#, Ctrl+4 is much better :) Since v1.9 also supports regions if CodeRush does not parse them (e.g. #pragma region in C++), so you can now even collapse C++ regions.

    CR_Commenter Expand Region

  • Toggle collapsing and expanding everything at top level
    Hotkey: Ctrl+5
    Will expand or collapse all regions in the current file at once. First time pressed it expands everything, second time everything is collapsed, good for getting a quick file overview. Note that again Visual Studio supports collapsing all sections with Ctrl+M+L, but this really sucks because all methods, xml comments and everything else is collapsed too and expanding all of that again is a pain. Again, Ctrl+5 is much better.

    CR_Commenter Collapse All Regions

  • Update all comments and xml blocks for the whole file.
    Hotkey: Ctrl+6
    Similar to Ctrl+1, but will not only process the current section, but the whole file. This generates and updates all comments in the whole file, updates the header comment and makes sure all Xml comment blocks are generated if missing (see below).

    CR_Commenter Force Commenting

  • Update just the header comment.
    Hotkey: Ctrl+8
    Force updating the header comment, can be useful to test new headers and to remove any old header data. Also updates the created and modified timestamps. Since v1.9 changing or having different header comments now works, but it will also remove any existing header comments to replace with the new header comment if the format is different. Also check out the new cool options in the Commenter options screen, which allows you to specify seperate header comments for each solution or project if you like.

    CR_Commenter Header

  • Generate xUnit or NUnit unit tests automatically for us
    Hotkey: Ctrl+9
    Generate xUnit unit tests automatically for us for all methods! All unit tests will have to be written and tested until everything is implemented correctly. Existing test methods will not be overwritten! You can either enable xUnit Tests (like in the screenshot below) or NUnit Tests.

    CR_Commenter Unit Tests

  • Remove All Comments, use this to cleanup a file
    Hotkey: Ctrl+0
    Remove All Comments, use this to cleanup a file. It will only remove generated comments (after } blocks). Also nice for testing and cleaning up other peoples code.

    CR_Commenter Remove Comments

  • Obfuscate whole file (removes ALL comments, xml comments, empty lines, headers, etc.)
    Hotkey: Ctrl+\
    Obfuscate whole file. All code will stay the same, but we will remove ALL comments, Xml Comments, empty lines, headers, etc. to reduce file size and make code harder to read. Not really a Commenter feature, but I implemented this for another app and it was easy to implement here too.

    CR_Commenter Obfuscate

  • Autogenerate XML Comments
    This feature will automatically add XML Comments to methods, properties, constructors, classes, etc. when the normal comments or generated. You can specify if you only want to generate XML comments for public methods or if you want to generate XML tags for all parameters (which was default on, but is default off now to produce much less empty xml parameter comments, which are usually not updated by the programmer anyway). CR_Commenter XML Comments

  • Other Improvements in v1.9
    • Added cool new per-project and per-solution header comments

    • New shortcut Ctrl+6 to just update the header

    • Added lower limit for block size to reduce comments for small blocks (also works recursive)

    • Added new features and hotkey logic, everything now easier to customize

    • Easier visibility if plugin is installed and working (more visual events, better options screen)

    • Fix generating comments for new files (messes up the using directives region), seems to be related to generating header comments

    • Improve line length count for removing comments (good new standard: 5 lines)

    • Fixed several problems in Format-Strings or SQL code.

    • Added whole method identifier from the accessibility keyword to the closing parenthesis for region directives.

    • Improved the CodeRush-autogenerated regions (Ctrl+3), make sure comments are not messed up

    • New default: Do not generate Xml parameter comments (mostly not used for better documentation anyway, adding them yourself for more details is more comfortable)

    • Added C++ "pragma region - #pragma endregion" support (thanks for testing to Enrico)

    • C++: include includes into using directives region

    • C++: Fixed header comment (project name could not be found)

    • C++: Do not use xml comments, just leave normal comments!

    • Fixed wronly generated regions outside of our plugin

    • C++: Fixed #includes region, do not modify include lines

    • Fixed region generation when CodeRush generates region before us!

    • Fixed header in case some other header comment is used

    • Added multiline header comment support for // /* */ -- and # to support all kinds of languages and comment formats

    • Also force generate comments on properties (methods work, also test constructors)

    • C++ allow Ctrl+4 and Ctrl+5 for C++ region collapsing (using visual studios Edit.ToggleOutliningExpansion and Edit.ToggleAllOutlining as fallbacks)


  • Older and Improved Features (from v1.8 and before):
    • Fixed '}' inside string or comment, even if CodeRush has not processed the whole file yet (double checking now). E.g. the following string will not longer produce any comments:
      SQL = String.Format("UPDATE GC SET Status = {0} WHERE ID = {1}", Msg, ID);

    • Supporting to generate regions again, but split up function names, also suppress CodeRush region generation with Ctrl+3, Ctrl+R (also suppressing VS hotkeys now) Better support for auto generated region blocks for big methods, just press Ctrl+3 to generate region (let CR_Commenter choose the name automatically, allow editing after pressing Ctrl+3).

    • Fixed #if DEBUG in Using directives (caused trouble, was removed)

    • Also added code to always remove #if DEBUG and #endif, especially at the beginning and end of the Using directives region

    • Fixed double line #using Using directives issue when using directives got mixed up.

    • Fixed long line commenter generation, will now still work, but not longer cut of any of your code. If you have long code lines (>100 letters), you can still generate comments for them and the Commenter will not longer cut anything off (you are responsible for formating the code).

    • Fixed auto-collapsing of regions, especially when closing blocks in methods, the method should not auto-collapse!

    • Checked support with other languages (which are at least similar to C#) like Lua, Python, etc.

    • In Python/Lua, etc. we had to disable } comment generation and also use other comments (# for python, -- for lua) when using Ctrl+1 for header generation.

    • Test all other hotkeys and make sure they do not affect the code badly in CodeRush unsupported languages (note: VB is still not supported since it is very different from c# code).

    • Supports now any unit test framework, actually does not care anymore what you use, only the unit test generation code will add Xunit, but you can easily change that code if you want to.

    • Options are now available directly into Editor tree of the CodeRush options!

    • Refactored all classes, added about 4 new classes and removed 1 unused class. The source code is now much easier to read, to follow and to change.

    • Remove commented out code, cleaned up the source code a lot.

    • And finally applied the commenter to the commenter source code :)

I would also like to thank all the people that wrote me emails, use the plugin, all my colleagues that use this tool everyday too (shoutout to Enrico Cieslik and Henning Thöle, and also Fabienne Khöle) and the following people who have contributed with ideas, code improvements and helpful bug reports (they are also mentioned in the TODO.txt file for their specific bug report or improvement idea): Andy (mangledmonster), Andrew Jones, Gil Yoder, Alexey Romanov, Enrico (Judge), Ed Blankenship, Sven Heitmann, Jan Diederich, Rob Heyes, Dan Avni, Andy Jacobs and everyone else :)
Comments [3] | | # 
 Sunday, November 02, 2008
Sunday, November 02, 2008 9:06:01 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | IronPython | Programming | Reviews | Silverlight | XNA )
I watched quite a lot of PDC 2008 Talks this week and I really like that they put all that stuff online for everyone to watch. To learn more about the PDC, check out event photos or see all the sessions, directly go to http://www.microsoftpdc.com/. Another good list of all sessions can be found here: http://coolthingoftheday.blogspot.com/2008/10/pdc2008-quick-video-link-list.html

Here are my personal favorite session recordings (from what I have seen, I have not seen all talks):

The Future of C# (by Anders Hejlsberg)
http://channel9.msdn.com/pdc2008/TL16/
As always Anders is fun to watch and you learn a lot about C#, the new dynamic keywords and whats to come. Not really stuff you will probably use right now, but good to know what we can do and probably will do in a few years. In case you want to watch IronPython or IronRuby, this session is a good prerequisite too.

Deep Dive: Dynamic Languages in Microsoft .NET
http://channel9.msdn.com/pdc2008/TL10/
Jim Hugunin (the creator of IronPython and DLR) talks about IronPython and the DLR (Dynamic Language Runtime) in general. Fun to watch, interesting insights and generally always interesting to anyone interesting in dynamic languages.

Panel: The Future of Programming Languages
http://channel9.msdn.com/pdc2008/TL57/
I like Panels like this and they had really great guests: Gilad Bracha, Douglas Crockford, Anders Hejlsberg, Erik Meijer, Wolfram Schulte, Jeremy Siek. They get a little bit off-topic at the end, but overall an interesting and very fun to watch discussion with a lot of interesting arguments on all sides (lot of different languages are discussed).

IronRuby: The Right Language for the Right Job
http://channel9.msdn.com/pdc2008/TL44/
I have learned Ruby few months ago and used it for a few days, but I never really liked it and continued to use Python instead, but John Lam (the IronRuby inventor) is a cool dude and give a lot of tips and shows off a lot of cool demos, which are equally interesting even if you would use some other dynamic language than Ruby.

Microsoft Visual C# IDE: Tips and Tricks
http://channel9.msdn.com/pdc2008/TL46/
Interesting and useful Tricks, Dustin shows off a lot of shortcuts and the whole presentation is in Visual Studio, which is cool.

Visual Studio Debugger Tips & Tricks
http://channel9.msdn.com/pdc2008/TL59/
I like Tips & Tricks talks and this one is good too. Informative and fun to watch as well. I will probably watch it again some time soon, like I will with some of the other talks too (because I usually code or do something else while the videos are running on a secondary monitor ^^).

Mono and .NET (by Miguel de Icaza)
http://channel9.msdn.com/pdc2008/PC54/
One of the last talks I discovered after watching many other talks. At first I thought this might be not really interesting for me, since this will probably focus only on Linux and Mono development, but I could not be more wrong. This was a really great talk, Miguel is such a great presenter, even when things go wrong, he makes fun remarks and is always on the topic. He gives great insight of game programming on Mono for Linux, the Mac and even the iPhone, which sounded really cool to me. Other topics were also interesting and fascinating.

Managed Extensibility Framework: Overview
http://channel9.msdn.com/pdc2008/TL33/
If you were every interesting in extending Visual Studio or want to hear about good ideas on how to extend frameworks or applications, this talk is helpful and insightful.

Visual C++: 10 is the New 6
http://channel9.msdn.com/pdc2008/TL13/
I'm not doing much C++ coding anymore, but many years ago this was my main programming language and it is still the most used language for games. This talk is highly informative and Boris Jabes talks in detail about new C++ Features in VS2010 and what is possible, what will get better and what even features will come in the future after VS2010. Also a lot of interesting questions and answers at the end.

The two talks Parallel Programming for C++ and Parallel Programming for Managed Developers were also highly informative and gave us programmers some good ideas on how to make better use of Multi-core systems. I especially like the Talk Parallel Library (TPL), which is insanely easy to use:
Parallel.For(0, 100, i => a[i] = a[i] * a[i]);

Microsoft XNA Game Studio: An Overview
http://channel9.msdn.com/pdc2008/TL43/
Frank Savage from the XNA Team talks about XNA Game Studio 3.0, which is highly informative if you don't know much about XNA yet. For me there was not much new to learn in this talk, but Frank is such a cool guy and it is fun to watch how he presents his stuff, which is one more reason to watch this talk. He also shows off some XNA games and the recently XNA Dream Build Play contest winner games.

The keynotes from Day 1 (Azure, Infos) and Day 2 (Windows 7, Visual Studio 2010 and .NET 4) were also interesting to watch. Other sessions sounded also interested and from what I have seen Oslo, Azure, Silverlight, Unit Testing in VS2010 and other new stuff is cool too, but I have no plans to use that stuff anytime soon (maybe Oslo for parsing text, which is easy and cool).

I hope you enjoy these videos as much as I do, definitely a great idea to put them all online :)

Comments [0] | | # 
 Sunday, October 12, 2008
Sunday, October 12, 2008 4:15:53 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | XNA )
Since I'm not posting many XNA articles lately, lets announce some stuff from Ziggyware :)


Ziggyware Fall 2008 XNA Article Contest


Start date: October 12'th 2008
Ending Date: November 30'th. (Midnight CST)

Contest Subjects
  • Write an XNA related tutorial for XNA developers (see examples)

Important: Read How to create an article for Ziggyware - Entries are subject to approval and may be disqualified per disgression.

Prizes
  • First Place
    • XBox 360 Elite (USA NTSC)
    • One Year subscription to the Microsoft XNA Creators Club
  • Second Place
    • One Year subscription to the Microsoft XNA Creators Club
More information can found at Ziggyware.com:
http://www.ziggyware.com/news.php?readmore=905
Comments [0] | | # 
 Saturday, September 13, 2008
Saturday, September 13, 2008 10:54:56 PM (GMT Standard Time, UTC+00:00) ( All | Game Development | Other | Programming | Reviews | XNA )
3sat Hi Folks, sorry for not updating my blog for such a long time. I basically had no time and not much interesting to report. Since I will focus more on game programming again in the near future, more blogging might happen again :)

Anyway, yesterday I was an interview partner on a German TV show called "neues", which can be seen on TV Sundays each week at 16:30 (4:30 PM). The topic is user generated content and I will answer some questions in the second half of the show. Since the show is not technical, it might not be of much interest for game developers, but hopefully the general public can learn a bit about XNA and the cool opportunities everyone has with developing games on their Xbox 360 or Windows PC.

It was a very interesting experience seeing how a show gets created, how many people are involved and how often it is rerecorded until everyone is happy. The people at ZDF/3sat were also very nice and it was fun to work with them.

The show will also be available on the 3sat video archive online (probably in the next few days):
http://www.3sat.de/mediathek/mediathek.php?query=*&red=neues&mode=search

Update 2008-09-14: Direct link to the video from the 3sat mediathek:
http://www.3sat.de/mediathek/?obj=9543

Some pics from my journey to Mainz where the TV show was recorded. It was quite a long trip taking about 5 hours reaching Mainz. At Frankfurt I moved from the ICE (fast train, but the power supply did not work so I could not even use my laptop, grr) to a slower train called RE (which does not supply any power to the train riders).


The weather was not very enjoyable Friday. In the morning it was raining very much.


Since I was not allowed to take any pictures inside the studio and due the bad weather I took not many photos. Hopefully I can post a small video once the 3sat video from the show is available.


Getting back home was the same ride in reverse. I did not spend much time in Mainz or Frankfurt due the bad weather and I was kinda tired so I played a few games I wanted to check out (like Spore and Space Siege) in the train. This ICE at Mainz was going in the opposite direction, seems like not many ICE trains drive from Mainz to Frankfurt, so I had to take a slow RE train again.


And finally in Frankfurt at the MUCH bigger train station. They have about 30 rail tracks there and you can see a lot of people wearing business suits. Probably all bankers and investors, hehe ^^

Okay, that's it. Maybe I will finish up some blog posts I have written in the last months, but never finished them due lack of time and motivation. Take care!
Comments [2] | | # 
 Thursday, May 08, 2008
Thursday, May 08, 2008 8:34:02 AM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | Reviews | XNA )
As you can read on the XNA Team Blog the first CTP (kinda early beta/alpha) for XNA Game Studio 3.0 is available for download now.
The major new features beside full VS 2008 (Express, Standard, Pro, etc., but not longer VS 2005) support you can now write and deploy games for the Zune. Luckily for me I own one (not many non-US people do since the Zune is ONLY available in the US). Please note that while the Zune is a powerful device it obviously will NOT support any advanced rendering techniques at all, but all the other parts of the XNA Framework are supported very well. This means 3D games or shader based games will be possible on the Zune and the display is just 320x240 pixel and there are other things you have to think about (controls and playing music), but other than that it should be pretty easy to start developing Zune games right away :)

While the new features are really cool and I definitely will check out the Zune development options (XNA MVPs already saw some cool samples and stuff at the MVP Summit about this ^^), the show stopper for me is the "no 64 bit support" issue. All my PCs (work, home, laptop and even the servers) run on 64 bit vista/win2003/win2008, it will not be easy to play around with XNA 3.0 ("Hey, go away from that PC, I wanna test something").

Anyway, check out XNA Game Studio 3.0, especially if you own a Zune and find this Zune-targeted game development cool. Other features worth checking out are better sound APIs in the new Media namespace, namely the SoundEffect and MediaPlayer APIs. They allow you to play sounds and music much easier and in more formats (yeah, mp3s).

Comments [0] | | # 
 Friday, April 18, 2008
Friday, April 18, 2008 6:13:40 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews | XNA )
Thursday was the last day of the MVP Summit and the closing keynotes by Ray Ozzie and Steve Balmer were in the Seattle Convention Center again. Additional sessions were hold in the hotels later that day.



After eating some breakfast it was time for the final keynotes.

Ray Ozzie told us how great we are and told some not so exciting development and technology stories. Maybe it was still too early to listen to anything but jokes.

A short break and preparing the mind for the upcoming Steve Balmer speech.

The weather outside is as usual. I guess I was really lucky last Saturday, which was the nicest day wheaterwise I had all year.

Say hello to Steve Balmer.

Steve is moving so fast and always so excited about everything, it is hard to take a still picture of him.

Mr. Balmer is wondering about Microsoft's Strategy? Not really, he is just straching his head :)

Looks like he is giving up on the Online Business ^^ He was actually talking about the fact that Microsoft is only number 3 in Online Search. But as much as he wishes to change that I predict Google will be on the top for a long time to come.

Now that is a lot of products!

Time for some questions.

Poor Steve did not only have to answer 3 Sharepoint/Groove questions in a row, but then the Canadians went crazy once again and demanded that he will wear a stupid shirt like the rest of the Canadians.

Which Steve Balmer actually went along with ^^ Some questions were really stupid now, but Steve stayed cool and always had funny remarks.

More gifts for Balmer!

And this is what he looked like at the end. Well, it was a nice MVP Summit and most sessions and keynotes were exciting and fun to watch.

Time to get up and eat some lunch. But it is only 11:30, why eat lunch already .. crazy.

Still about 1700 MVPs around.

The Canadians couldn't stop making noise and drawing attention. Time for a final group photo. Other MVPs told me that this is not the first time they acted that strange and it was not funny anymore. Just stay normal guys, this is not a sporting event.

At lunch all the XNA/DirectX MVPs meet for a last time. We put 2 tables together and relaxed for a while until it was time to eat lunch.

Someone is taking a picture of us. I guess Andy Dunn will upload his pictures somewhere too, I will check out his blog later.

A lot of Asian guys were also taking pictures of themselfs :)

Saying bye to Pat and Andy Dunn and Andys Camera :)

Leaving the Conference area for the last time. Still only XNA/DirectX MVPs on this photo.

Nick Gravelyn is telling us about his next 100 game ideas.

And George Clingerman, Catalin Zima and me are listening.

And one optinoal session I attended to in the afternoon at the hotel. It was about the VS Debugger and VS 2008 SP1 and was very informative and exciting. Especially through the fact that the audience was quite loud about known bugs and issues we have every day.

Good. I made it, time for me to leave to the airport now. It will be a long trip back. It was a nice time here in Seattle, but I will be glad to be home again and not having to type on my laptop keyboard anymore :)
Comments [0] | | # 
Thursday, April 17, 2008 11:27:33 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews | Silverlight | XNA )
Wednesday Sessions were on the Microsoft Campus again. I was most of the time this day in Building 85 (same as yesterday) and listening and participating to XNA discussions. In the evening the fun MVP Attendee Party at the Experience Music Project at the Seattle Center, which was a lot of fun.



The day started again with taking the bus to the Microsoft Campus again. Since I woke up a little bit later today I ate half of my breakfast in the bus :)

The XNA Sessions are starting and everyone is checking emails and stuff. Took like 4 years to download 1 kb of data.

After the lunch break someone suggested that we go over to Building 84 and check out the "Wall of graphic cards", which was pretty cool, but we were not allowed to take photos for some strange reason.

This is Mateusz Kierepka from Poland, another XNA MVP, who thinks like me that Silverlight and XNA should merge sooner than later :) Sadly the XNA Team is not thinking the same way yet.

Leaving Building 85 after the final session ..

And heading back to the hotels.

Once there after preparing the post from the day before I headed out for the Party at the Seattle Center park.

And I was taking the Monorail once again, which is quite a bit faster than my feet.

The Seattle Space Needle at night (or at evening). Still people driving up and down ..

At the party we were again not allowed to take any pics of the Science Fiction Museum or the History of Music exhibition. And my camera batteries being low didn't help either.

Most other pictures of today were either blurry or boring. Sorry for not much more. Hopefully the Camera will be at good enough health tomorrow. I do not have anymore replacement batteries :(

And tomorrow some pictures of the final keynotes by Ray Ozzie and Steve Balmer and some final goodbye pictures.
Comments [1] | | # 
 Thursday, April 17, 2008
Thursday, April 17, 2008 2:26:38 AM (GMT Standard Time, UTC+00:00) ( All | Conferences | Game Development | Other | Reviews | XNA )
Tuesday's Sessions were happening on the Microsoft Campus. Since the XNA MVP Party yesterday and waking up this morning (which is actually Wednesday) I had no time to prepare and publish this blog post. On the Microsoft Campus, especially in Building 85 (XNA Guys) there was no way I could upload this because Internet was just way too slow. So this post had to wait until I got back to the Hotel after the Attendee party.



This morning we could eat breakfast at the Hotel in this huge ballroom.

Walking out of the hotel to the bus, which will take us to the 15km distant Microsoft's Campus.

The weather is pretty much the same as on Monday or Sunday. Driving up north here until we reach the bridge over to Kirkland and Redmond.

After about a 40 minute drive we arrived at the Microsoft Conference Center (Building 33). Traffic is not going very fast over here ^^

This is how it looks inside the Microsoft Conference Center (short MSCC) at 9:00 in the morning when a conference is going on.

Instead of attending the first XNA session I decided to take a look at the Silverlight 2.0 talk by the master himself: Scott Guthrie

Until someone decided to throw down this wall, which causes quite a bit of noise and amusement.

And this was me uploading the post for Monday yesterday while listening to the sessions.

After eating some lunch and then some cake (see picture) I watched some StarCraft vods from GOM TV with the good old Nick 'Tasteless' commenting in English. This was the game with Tossgirl, which was very exciting to watch.

Shortly after that another session about ASP.NET MVC started with Scott Hanselman.

The MVC talk was very enjoyable to watch because of the many remarks Scott made.

As you can see, the MVC framework is still very much a work in progress.

Okay, let me take a look at that. Hmm, that should test the MVC routing engine. While Scott was talking about that I tried to write some MVC tests with Xunit, which worked just great (as advertised).

Setup-Code for some routes, looks complicated, but I'm not sure how to make them much easier.

I went to Building 85 now for more XNA sessions over there. It was raining quite a bit.

One of the buses traveling from building to building at the Microsoft Campus.

A few native DirectX MVPs, which probably make fun of us managed DirectX/XNA MVPs. But it is all in good fun, we all like DirectX somehow :)

Heading back to Seattle through all the traffic.

Instead of heading back to the Hotels all XNA/DirectX MVPs had a nice little dinner at Jillian's, which obviously did not have enough power to light up both parts of their logo.

Andy Dunn (The ZMan), Sean ?, Michael Klucher (from the XNA Team) and George Clingerman (Mr. www.XnaDevelopment.com) standing around and discussing the physics of billiard :)

Michael Klucher, Nick Gravelyn and Niko Sumi? sitting down and discussing more XNA.

And finally heading back to the Hotel after we were thrown out from Jillian's ^^

More pictures today from the second and last day at the Microsoft Campus. Cya!
Comments [0] | | # 
 Thursday, April 10, 2008
Thursday, April 10, 2008 10:31:19 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Other | Programming | Racing Game | Rocket Commander | XNA )
After updating so much on my blog the last 2 days and posting all the unfinished posts I had prepared a few weeks ago I finally reach the last important one I wanted to make. But due the fact that I will fly to the MVP summit tomorrow I better not stay away too long. Hopefully this post still contains all the vital information. Let's get started.

First of all you need the latest version of the free NSIS (Nullsoft Installer System):
There are TONS of information on that website and if you are new to the NSIS scripts be sure to read at least the quick start guide (called simple tutorials) and use the NSIS Forums search feature for more specific problems and questions.

Good, now just let us go quickly through the .nsi file I currently use for all my XNA Game Setups. Please note that this file has evolved over many years (I guess I started using NSIS in 2002) and parts might not be simple to read or even be how it is done today, but the script was always a very helpful and quick way to create Setups for me. While detecting all the requirements to make XNA work is not a piece of cake many NSIS tutorials helped me to figure all the parts out.

The following .nsi script is from the previous blog post "Loading Collada Models in XNA 2.0 and doing Skinning and Animation" and was used to build the XnaSkinningColladaModelsSetup.exe (2.2 MB) file. If you want to use it for your own XNA Game project just use a text-editor and replace "XnaSkinningColladaModels" with your own game name (it occurs many times in the file). Also create a .bmp file with the name of your game for the setup header (check MUI_HEADERIMAGE_BITMAP) or comment the 2 Header Image lines out with a ; or # if you do not want any header image. All files are grabbed from the /Release directory and will be installed to the target (make sure that you only have files in the /Release directory that you want to have deployed). This should be already enough information to make your setup work, the rest of this article will just go into the details of the script.

The XnaSkinningColladaModelsSetup.nsi file is organized in 6 parts (Initialization, onInit, File section, Optional sections, Helper functions and Uninstall):
  • Initialization: Just sets all the names, the required modules, and how the setup is displayed to the user (all the pages, see MUI_PAGE). Version information and file information are also a vital part here and you should enter some meaningful values here. Also put any language strings for different languages (this installer is just using english text) at text part of the initialization code.
    !include "MUI.nsh"
    SetCompressor /SOLID lzma
    
    ;Name and file
    Name "XnaSkinningColladaModels"
    OutFile "XnaSkinningColladaModelsSetup.exe"
    
    ;Default installation folder
    InstallDir "$PROGRAMFILES\XnaSkinningColladaModels"
    
    ;Get installation folder from registry if available
    InstallDirRegKey HKLM "Software\XnaSkinningColladaModels" ""
    
    !define MUI_COMPONENTSPAGE_NODESC
    
    ;Interface Settings
    !define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install-blue.ico"
    !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall-blue.ico"
    !define MUI_HEADERIMAGE
    !define MUI_HEADERIMAGE_BITMAP "XnaSkinningColladaModels.bmp"
    !define MUI_HEADERIMAGE_UNBITMAP "XnaSkinningColladaModels.bmp"
    !define MUI_ABORTWARNING
    
    ;Pages
    !insertmacro MUI_PAGE_WELCOME
    !insertmacro MUI_PAGE_COMPONENTS
    !insertmacro MUI_PAGE_DIRECTORY
    !insertmacro MUI_PAGE_INSTFILES
    
    !insertmacro MUI_UNPAGE_CONFIRM
    !insertmacro MUI_UNPAGE_INSTFILES
    
    ;Languages
    !insertmacro MUI_LANGUAGE "English"
    
    ;Version Information
    VIProductVersion "1.0.0.0"
    VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "XnaSkinningColladaModels"
    VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "XnaSkinningColladaModels project, see http://BenjaminNitschke.com"
    VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "BenjaminNitschke.com"
    VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "© BenjaminNitschke.com 2007"
    VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "XnaSkinningColladaModels"
    VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "1.0.0.0"
    
    ; The text to prompt the user to enter a directory
    ComponentText "This will install XnaSkinningColladaModels for XNA 2.0."
    
    UninstallText "This will remove XnaSkinningColladaModels for XNA 2.0 from your computer."
    
    ; New .NET Check function, we want at least v2.0
    !define DOT_MAJOR "2"
    !define DOT_MINOR "0"
    
    ; The text to prompt the user to enter a directory
    DirText "Please select a directory for the installation:"
    
    AutoCloseWindow true
    

  • .onInit: This method is executed before the installer is run and can perform any checks you like. I just try to find out if any c:\games directory exists in several languages, if it does, then we can use it, else just use the default c:\program files location.
    
    Function .onInit
        ;First try to find "Games" (eng), "Spiele" (ger), "jeux" (fr),
        ; "giochi" (ita), "jogos" (port) or "spelen" (nl) directory!
        StrCpy $R1 "C:\Games"
        IfFileExists $R1 UseIt
        StrCpy $R1 "C:\Spiele"
        IfFileExists $R1 UseIt
        StrCpy $R1 "C:\jeux"
        IfFileExists $R1 UseIt
        StrCpy $R1 "C:\giochi"
        IfFileExists $R1 UseIt
        StrCpy $R1 "C:\jogos"
        IfFileExists $R1 UseIt
        StrCpy $R1 "C:\spelen"
        IfFileExists $R1 UseIt
        Goto DontUseIt
    UseIt:
        StrCpy $INSTDIR "$R1\XnaSkinningColladaModels"
    DontUseIt:
    FunctionEnd
    

  • File section: This is the most important section and always required because it tells the setup where the files come from (in our case from the Release sub directory) and where they should be installed (in #INSTDIR).
    
    ; The stuff to install
    Section "XnaSkinningColladaModels"
      SectionIn RO
      ; Set output path to the installation directory.
      SetOutPath $INSTDIR
      ; Put file there
      File /r "Release\*.*"
     
      ; Write the installation path into the registry
      WriteRegStr HKLM "SOFTWARE\XnaSkinningColladaModels" "Install_Dir" "$INSTDIR"
      WriteRegStr HKLM "SOFTWARE\XnaSkinningColladaModels" "Version" "v1.0"
      ; Write the uninstall keys for Windows
      WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XnaSkinningColladaModels" "DisplayName" "XnaSkinningColladaModels"
      WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XnaSkinningColladaModels" "UninstallString" "$INSTDIR\uninstall.exe $INSTDIR"
      WriteUninstaller "uninstall.exe"
    SectionEnd
    

  • Optional sections: These sections are optional and the user can decide not to create desktop shortcuts, not to install XNA 2.0 if it is not found, etc. I usually leave them all on by default because they make sense. Most important here are the DirectX and XNA prequisites sections, which make a lot of use of the helper functions.
    
    ; Optional sections
    Section "Create Start-Menu shortcuts"
      StrCpy $R1 "$SMPROGRAMS\XnaSkinningColladaModels"
      CreateDirectory "$R1"
      CreateShortCut "$R1\XnaSkinningColladaModels.lnk" "$INSTDIR\XnaSkinningColladaModels.exe" "" "$INSTDIR\XnaSkinningColladaModels.exe" 0 "" "" "XnaSkinningColladaModels"
      CreateShortCut "$R1\XnaSkinningColladaModels Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 "" "" "Uninstall XnaSkinningColladaModels."
      CreateShortCut "$R1\BenjaminNitschke.com.lnk" "http://BenjaminNitschke.com" "" "" 0 "" "" "BenjaminNitschke.com website from the creator of this game"
    SectionEnd
    
    Section "Create Desktop shortcuts"
      CreateShortCut "$DESKTOP\XnaSkinningColladaModels.lnk" "$INSTDIR\XnaSkinningColladaModels.exe" "" "$INSTDIR\XnaSkinningColladaModels.exe" 0
    SectionEnd
    
    Section "Check for .NET 2.0 Framework (required)"
      Call IsDotNETInstalled
      Pop $0
      StrCmp $0 1 found.NETFramework no.NETFramework
    no.NETFramework:
    ;MessageBox MB_OK ".NET was not found .."
      ; First of all check for correct IE version!
      Call CheckForCorrectIEVersion
      ; First check if DotNetFx.exe exists
      IfFileExists "$EXEDIR\DotNetFx.exe" 0 DotNetFileNotFound
      ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" "AutoAdminLogon"
      ExecWait '"$EXEDIR\DotNetFx.exe" /q'
      StrCmp $R0 1 RestoreAutoLogon End
    RestoreAutoLogon:
      WriteRegStr HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" "AutoAdminLogon" "1"
      goto End
    
      ; If DotNetFx.exe was not found, try downloading online
    DotNetFileNotFound:
      MessageBox MB_YESNOCANCEL|MB_ICONQUESTION ".NET Framework 2.0 was not found. XnaSkinningColladaModels will not run without it! $\r$\nWould you like to automatically download and install it? $\r$\n $\r$\nChoose Yes for an automatic download and installation. $\r$\nChoose No, you want to select the download yourself (e.g. locally if already downloaded). $\r$\nMicrosoft's .NET Framework Download page will open up! $\r$\nChoose cancel to abort any action here, you will have to install the .NET Framework (2.0) $\r$\nyourself. That can be done for example automatically by the Windows Update $\r$\n(just go to http://windowsupdate.microsoft.com )" IDYES DL IDNO IndirectDL
      goto End
      IndirectDL:
        ExecShell open "http://www.microsoft.com/downloads/details.aspx?FamilyID=0856eacb-4362-4b0d-8edd-aab15c5e04f5" "" SW_SHOWNORMAL
        goto End
      DL:
        Call ConnectInternet ;Make an internet connection (if no connection available)
    
        StrCpy $2 "$TEMP\DotNetFX_Setup.exe"
        NSISdl::download http://download.microsoft.com/download/5/6/7/567758a3-759e-473e-bf8f-52154438565a/dotnetfx.exe $2
        Pop $0
        StrCmp $0 success success
          SetDetailsView show
          DetailPrint "download failed: $0"
          MessageBox MB_OK ".NET could not be installed, download was aborted. Please install .NET yourself! $\r$\nRest of the installation went ok, you just need .NET 2.0 to start XnaSkinningColladaModels!"
          goto End
        success:
          ; Install, in quiet mode (will do everything automatically)
          ExecWait '"$2" /q'
          Delete $2
      
        goto End
    found.NETFramework:
    ;MessageBox MB_OK ".NET is installed, very good ^^"
    End:
    SectionEnd
    
    Section "Check for DirectX 9.0c (November 2007) (required)"
      call IsDirectX9Installed
      pop $0
      StrCmp $R1 1 maybefound.DirectX9 notFound.DirectX9
    maybefound.DirectX9:
        ; Check if managed directx december 2006 is installed!
        IfFileExists "$WINDIR\Microsoft.NET\DirectX for Managed Code\1.0.2911.0" found.DirectX9  
    notFound.DirectX9:
    ;MessageBox MB_OK "DirectX9 or better was not found!"
      ; First check if a file "dx90update_redist.exe" exists
      IfFileExists "$EXEDIR\directx_nov2007_redist.exe" 0 DX9RedistFileNotFoundTest1
      ExecWait '"$EXEDIR\directx_nov2007_redist.exe" /Q /T:"$TEMP" /C'
      ; Now install DirectX9
      ExecWait "$TEMP\dxsetup.exe"
      goto End
    DX9RedistFileNotFoundTest1:
        ; Check again in temp folder
      IfFileExists "$TEMP\directx_nov2007_redist.exe" 0 DX9RedistFileNotFound
      ExecWait '"$TEMP\directx_nov2007_redist.exe" /Q /T:"$TEMP" /C'
      ; Now install DirectX9
      ExecWait "$TEMP\dxsetup.exe"
      goto End
      ; If that failed, ask user!
    DX9RedistFileNotFound:
      MessageBox MB_YESNOCANCEL|MB_ICONQUESTION "DirectX9.0c November 2007 or better was not found. XnaSkinningColladaModels needs DirectX9.0c and Managed DirectX runtimes to run. $\r$\nWould you like to automatically download and install it? $\r$\n $\r$\nChoose Yes for an automatic download and installation. $\r$\nChoose No, you want to select the download yourself (e.g. in another language than english). $\r$\nMicrosoft's DirectX Download page will open up! $\r$\nChoose cancel to abort any action here, you will have to install DirectX yourself." IDYES DL IDNO IndirectDL
      goto End
    IndirectDL:
      ExecShell open "http://www.microsoft.com/downloads/details.aspx?familyid=1A2393C0-1B2F-428E-BD79-02DF977D17B8" "" SW_SHOWNORMAL 
      goto End
    DL:
      Call ConnectInternet ;Make an internet connection (if no connection available)
    
      StrCpy $2 "$TEMP\directx_nov2007_redist.exe"
      NSISdl::download http://download.microsoft.com/download/c/6/a/c6af7ea3-f85c-4fef-a475-5810f82def1f/directx_nov2007_redist.exe $2
    
      Pop $0
      StrCmp $0 success success
        SetDetailsView show
        DetailPrint "download failed: $0"
        MessageBox MB_OK "DirectX could not be installed, download was aborted. Please install DirectX yourself! $\r$\nRest of the installation went ok, you just need DirectX 9.0c December 2006 to start XnaSkinningColladaModels!"
        goto End
      success:
        ; Install quietly into temp folder
        ExecWait '"$2" /Q /T:"$TEMP" /C'
        ; Now install DirectX9
        ExecWait "$TEMP\dxsetup.exe"
        ; Yeah, we made it!
        ;why delete, maybe user wants to keep it ^^ Delete $2
      goto End
    found.DirectX9:
    ;MessageBox MB_OK "Found DirectX9 or better .."
      goto End
    End:
    SectionEnd
    
    Section "Check for XNA 2.0 (December 2007) (required)"
      call IsXNAInstalled
      pop $0
      StrCmp $R1 1 found.XNA notFound.XNA
    notFound.XNA:
      MessageBox MB_YESNOCANCEL|MB_ICONQUESTION "XNA 2.0 (December 2007) or better was not found. The XnaSkinningColladaModels needs the XNA 2.0 runtimes to run. $\r$\nWould you like to automatically download and install it? $\r$\n $\r$\nChoose Yes for an automatic download and installation. $\r$\nChoose No, you want to select the download yourself (e.g. in another language than english). $\r$\nMicrosoft's XNA Download page will open up! $\r$\nChoose cancel to abort any action here, you will have to install XNA yourself." IDYES DL IDNO IndirectDL
      goto End
    IndirectDL:
      ExecShell open "http://www.microsoft.com/downloads/details.aspx?FamilyID=15fb9169-4a25-4dca-bf40-9c497568f102" "" SW_SHOWNORMAL 
      goto End
    DL:
      Call ConnectInternet ;Make an internet connection (if no connection available)
    
      StrCpy $2 "$TEMP\xnafx20_redist.msi"
      NSISdl::download "http://download.microsoft.com/download/b/8/a/b8a1f45a-63e6-4d1f-9c03-55a83d30ee3b/xnafx20_redist.msi" $2
    
      Pop $0
      StrCmp $0 success success
        SetDetailsView show
        DetailPrint "download failed: $0"
        MessageBox MB_OK "XNA 2.0 could not be installed, download was aborted. Please install XNA 2.0 yourself! $\r$\nRest of the installation went ok, you just need XNA 2.0 (December 2007) to start the XnaSkinningColladaModels!"
        goto End
      success:
        ; Install XNA, quietly does not work on vista for some reason ...
        ExecWait 'msiexec /i "$2" /q'
        ; Yeah, we made it!
        ;why delete, maybe user wants to keep it ^^ Delete $2
      goto End
    found.XNA:
    ;MessageBox MB_OK "Found XNA 2.0 or better .."
      goto End
    End:
    SectionEnd
    
    Section "-Ask some questions"
      MessageBox MB_YESNO|MB_ICONQUESTION "Start the XnaSkinningColladaModels now?" IDYES StartRC
      goto DontStart
    StartRC:
      Exec "$INSTDIR\XnaSkinningColladaModels.exe"
    DontStart:
    SectionEnd
    

  • Helper functions: These little helpers are the utitilies to make all the complex XNA installation steps work. They go through all the steps from making sure .NET is installed, installing it automatically for you if you like, even opening up an internet connection if you are on dialup, checking all the prequisites for installing .NET (like having at least IE 5) and then finally install all the XNA 2.0 and DirectX stuff that is required to run your game on top of that. All without ever leaving the installer and tested on many platforms. I usually do not touch this code unless I have to update some links or include a newer version of .NET, DirectX or XNA. I tried to comment my work, but understanding this script part will not be easy. Just do not expect that any changes you make here will be safe. You need to do a lot of testing in order to add additional functionality here.
    ; IsDotNETInstalled
    ;
    ; Usage:
    ;   Call IsDotNETInstalled
    ;   Pop $0
    ;   StrCmp $0 1 found.NETFramework no.NETFramework
    ;Function IsDotNETInstalled
    ;   Push $0
    ;   Push $1
    ;   Push $2
    ;   Push $3
    ;   Push $4
    ;
    ;   ReadRegStr $4 HKEY_LOCAL_MACHINE \
    ;     "Software\Microsoft\.NETFramework" "InstallRoot"
    ;   # remove trailing back slash
    ;   Push $4
    ;   Exch $EXEDIR
    ;   Exch $EXEDIR
    ;   Pop $4
    ;   # if the root directory doesn't exist .NET is not installed
    ;   IfFileExists $4 0 noDotNET
    ;
    ;   StrCpy $0 0
    ;
    ;   EnumStart:
    ;
    ;     EnumRegKey $2 HKEY_LOCAL_MACHINE \
    ;       "Software\Microsoft\.NETFramework\Policy"  $0
    ;     IntOp $0 $0 + 1
    ;     StrCmp $2 "" noDotNET
    ;
    ;     StrCpy $1 0
    ;
    ;     EnumPolicy:
    ;
    ;       EnumRegValue $3 HKEY_LOCAL_MACHINE \
    ;         "Software\Microsoft\.NETFramework\Policy\$2" $1
    ;       IntOp $1 $1 + 1
    ;        StrCmp $3 "" EnumStart
    ;         IfFileExists "$4\$2.$3" foundDotNET EnumPolicy
    ;
    ;   noDotNET:
    ;     StrCpy $0 0
    ;     Goto done
    ;
    ;   foundDotNET:
    ;     StrCpy $0 1
    ;
    ;   done:
    ;     Pop $4
    ;     Pop $3
    ;     Pop $2
    ;     Pop $1
    ;     Exch $0
    ;FunctionEnd
    
    ; Call IsDotNetInstalled
    ; This function will abort the installation if the required version 
    ; or higher version of the .NETFramework is not installed.  Place it in
    ; either your .onInit function or your first install section before 
    ; other code.
    Function IsDotNETInstalled
      Push $0
      Push $1
      Push $2
      Push $3
      Push $4
    
      StrCpy $0 "0"
      StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ;registry entry to look in.
      StrCpy $2 0
    
      StartEnum:
        ;Enumerate the versions installed.
        EnumRegKey $3 HKLM "$1\policy" $2
    
    ;MessageBox MB_OK "EnumRegKey $3"
        ;If we don't find any versions installed, it's not here.
        StrCmp $3 "" noDotNet notEmpty
    
        ;We found something.
        notEmpty:
          ;Find out if the RegKey starts with 'v'.  
          ;If it doesn't, goto the next key.
          StrCpy $4 $3 1 0
          StrCmp $4 "v" +1 goNext
          StrCpy $4 $3 1 1
          
          ;It starts with 'v'.  Now check to see how the installed major version
          ;relates to our required major version.
          ;If it's equal check the minor version, if it's greater, 
          ;we found a good RegKey.
          IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg
          ;Check the minor version.  If it's equal or greater to our requested 
          ;version then we're good.
          StrCpy $4 $3 1 3
          IntCmp $4 ${DOT_MINOR} yesDotNetReg goNext yesDotNetReg
    
        goNext:
          ;Go to the next RegKey.
          IntOp $2 $2 + 1
          goto StartEnum
    
      yesDotNetReg:
    ;MessageBox MB_OK "DotNet Reg stuff was found!"
        ;Now that we've found a good RegKey, let's make sure it's actually
        ;installed by getting the install path and checking to see if the 
        ;mscorlib.dll exists.
        EnumRegValue $2 HKLM "$1\policy\$3" 0
        ;$2 should equal whatever comes after the major and minor versions 
        ;(ie, v1.1.4322)
        StrCmp $2 "" noDotNet
        ReadRegStr $4 HKLM $1 "InstallRoot"
        ;Hopefully the install root isn't empty.
        StrCmp $4 "" noDotNet
        ;build the actuall directory path to mscorlib.dll.
        StrCpy $4 "$4$3.$2\mscorlib.dll"
    ;MessageBox MB_OK "Does $4 exists?"
        IfFileExists $4 yesDotNet noDotNet
    
      noDotNet:
    ;MessageBox MB_OK "DotNet was not found .."
        StrCpy $0 0
        Goto done
    
      yesDotNet:
    ;MessageBox MB_OK "DotNet stuff was found!"
        StrCpy $0 1
      done:
        Pop $4
        Pop $3
        Pop $2
        Pop $1
        Exch $0
    FunctionEnd
    
    Function ConnectInternet
      ClearErrors
      Dialer::AttemptConnect
      IfErrors noie3
    
      Pop $R0
      StrCmp $R0 "online" connected
        Strcpy $R0 "Cannot connect to the internet."
        MessageBox MB_OK|MB_ICONSTOP $R0
        Quit
    
      noie3:
    
      ; IE3 not installed
      MessageBox MB_OK|MB_ICONINFORMATION "Please connect to the internet now."
    
      connected:
    FunctionEnd
    
    ;GetIEVersion
    ; Based on Yazno's function, [url]http://yazno.tripod.com/powerpimpit/[/url]
    ; Returns on top of stack
    ; 1-6 (Installed IE Version)
    ; or
    ; '' (IE is not installed)
    ;
    ; Usage:
    ;   Call GetIEVersion
    ;   Pop $R0
    ;   ; at this point $R0 is "5" or whatnot
    Function GetIEVersion
       Push $R0
       ClearErrors
       ReadRegStr $R0 HKLM "Software\Microsoft\Internet Explorer" "Version"
       IfErrors lbl_123 lbl_456
    
       lbl_456: ; ie 4+
          ReadRegStr $R0 HKLM "Software\Microsoft\Internet Explorer\Version Vector" "IE"   
          Strcpy $R0 $R0 4
          Goto lbl_done
    
       lbl_123: ; older ie version
          ClearErrors
          ReadRegStr $R0 HKLM "Software\Microsoft\Internet Explorer" "IVer"
          IfErrors lbl_error
    
          StrCpy $R0 $R0 3
          StrCmp $R0 '100' lbl_ie1
          StrCmp $R0 '101' lbl_ie2
          StrCmp $R0 '102' lbl_ie2
    
          StrCpy $R0 '3' ; default to ie3 if not 100, 101, or 102.
          Goto lbl_done
          lbl_ie1:
             StrCpy $R0 '1'
             Goto lbl_done
          lbl_ie2:
             StrCpy $R0 '2'
             Goto lbl_done
          lbl_error:
             StrCpy $R0 ''
          lbl_done:
       Exch $R0
    FunctionEnd
    
    ; Checks for current IE version and will try to call
    ; IE6Setup.exe if it exists, otherwise prompt the user
    ; to install it for himself (at least IE5.01 is required for .NET)
    Function CheckForCorrectIEVersion
       Call GetIEVersion
       Pop $R0
       Push $R1
       StrCpy $R1 $R0 1
       StrCmp $R1 '6' lbl_IEOK
       StrCmp $R1 '5' lbl_IE5
       StrCmp $R1 '4' lbl_IEKO
       StrCmp $R1 '2' lbl_IEKO
       StrCmp $R1 '3' lbl_IEKO
       StrCmp $R1 '1' lbl_IEKO
    
       lbl_IE5:
          StrCmp $R0 '5.00' lbl_IEKO
          Goto lbl_IEOK
       lbl_IEKO:
    ;MessageBox MB_OK "IE5.01 or better was not found!"
          ; First check if ie6setup.exe exists
          IfFileExists "$EXEDIR\Setups\ie6sp1\ie6setup.exe" 0 IE6FileNotFound
          ExecWait '"$EXEDIR\Setups\ie6sp1\ie6setup.exe"'
          goto lbl_IEOK
          ; If DotNetFx.exe was not found, try downloading online
        IE6FileNotFound:
          MessageBox MB_YESNOCANCEL|MB_ICONQUESTION "IE5.01 or better was not found. .NET can't be installed without it! $\r$\nWould you like to automatically download and install it? $\r$\n $\r$\nChoose Yes for an automatic download and installation. $\r$\nChoose No, you want to select the download yourself (e.g. in another language than english). $\r$\nMicrosoft's .NET Framework Download page will open up! $\r$\nChoose cancel to abort any action here, you will have to install IE5.01 or better$\r$\nyourself, that can be done for example automatically by the Windows Update $\r$\n(just go to http://windowsupdate.microsoft.com )" IDYES DL IDNO IndirectDL
          goto lbl_IEOK
         IndirectDL:
          ExecShell open "http://www.microsoft.com/downloads/details.aspx?FamilyID=1e1550cb-5e5d-48f5-b02b-20b602228de6" "" SW_SHOWNORMAL
          goto lbl_IEOK
         DL:
          Call ConnectInternet ;Make an internet connection (if no connection available)
          StrCpy $2 "$TEMP\IE6Setup.exe"
          NSISdl::download http://download.microsoft.com/download/ie6sp1/finrel/6_sp1/W98NT42KMeXP/EN-US/ie6setup.exe $2
          Pop $0
          StrCmp $0 success success
          SetDetailsView show
          DetailPrint "download failed: $0"
          MessageBox MB_OK "IE6Setup could not be installed, download was aborted. Please install IE and .NET yourself! $\r$\nRest of the installation went ok, you just need IE for .NET to start XnaSkinningColladaModels!"
          goto lbl_IEOK
        success:
          ; Install, in quiet mode (will do everything automatically)
          ExecWait '"$2"'
          Delete $2
       lbl_IEOK:
    ;MessageBox MB_OK "IE5.01 or better was found .."
    FunctionEnd
    
    ; Check if DirectX9 or better is installed,
    ; will return $0 1 then, else $0 is 0.
    Function IsDirectX9Installed
      Push $0
    
      ReadRegStr $0 HKLM "SOFTWARE\Microsoft\DirectX" "Version"
      StrCpy $1 $0 4 3
      IntCmp $1 9 found.DirectX9 notFound.DirectX9 found.DirectX9
    notFound.DirectX9:
    ;MessageBox MB_OK "can't find direct9"
      StrCpy $0 0
      Goto done
    
    found.DirectX9:
    ;MessageBox MB_OK "found direct9 or better"
      StrCpy $0 1
    
    done:
    ;MessageBox MB_OK "direct9 found value: $0"
      StrCpy $R1 $0
      Exec $0
    FunctionEnd
    
    ; Check if XNA 2.0 or better is installed,
    ; will return $0 1 then, else $0 is 0.
    Function IsXNAInstalled
      Push $0
    
      ;Enumerate the versions installed.
      EnumRegKey $0 HKCU "SOFTWARE\Microsoft\XNA\Game Studio" "v2.0"
        ;if the root directory doesn't exist XNA is not installed
      StrCmp $0 "" notFound.XNA found.XNA
    
    notFound.XNA:
    ;MessageBox MB_OK "can't find XNA"
      StrCpy $0 0
      Goto done
    
    found.XNA:
    ;MessageBox MB_OK "found XNA or better"
      StrCpy $0 1
    
    done:
    ;MessageBox MB_OK "XNA found value: $0"
      StrCpy $R1 $0
      Exec $0
    FunctionEnd
    


  • And finally the uninstall section: This section will deinstall your game and get rid of all files in the directory that belong to the game, if there is more, it will not kill any other sub directories or even delete the game directory if there is other data present. If you want to be totally sure that only your game files are deleted, only include files that have been installed in this section via the Delete command. I used 2 RMDir /r commands to delete the Content and ColladaModels directories, which makes things much easier. But if for example the user made screenshots, they will be saved in a /Screenshots directory, which is not deleted here and will stay alive even after the uninstall is complete. There is also another check to make sure the game is still installed at this location by making sure the .exe file still exists. If the .exe file is missing, the uninstaller assumes the game is already uninstalled and it will only remove registry keys, but not any files.
    
    ; special uninstall section.
    Section "Uninstall"
      ; REMOVE UNINSTALLER
      Delete $INSTDIR\uninstall.exe
    
      ; remove registry keys
      DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XnaSkinningColladaModels"
      DeleteRegKey HKLM "SOFTWARE\XnaSkinningColladaModels"
    
      ; remove shortcuts, if any.
      Delete "$SMPROGRAMS\XnaSkinningColladaModels\*.*"
      Delete "$DESKTOP\XnaSkinningColladaModels.lnk"
      ; remove directories used.
      RMDir "$SMPROGRAMS\XnaSkinningColladaModels"
    
      ; First check if rocket commander is located here, if not, dont kill stuff!
      IfFileExists "$INSTDIR\XnaSkinningColladaModels.exe" 0 RCNotFound
      ; remove files
      Delete "$INSTDIR\XnaSkinningColladaModels.*"
      RMDir /r "$INSTDIR\Content"
      RMDir /r "$INSTDIR\ColladaModels"
      
      ; Try to kill main folder, but this will fail if there are still some files in it
      RMDir "$INSTDIR"
      goto End
      RCNotFound:
      MessageBox MB_OK "XnaSkinningColladaModels.exe was not found in $INSTDIR, won't delete any files, $\r$\nplease remove directory and files yourself (all registry keys were killed)."
      End:
    SectionEnd
    

Okay, we have now covered all code of the XnaSkinningColladaModelsSetup.nsi file, download it and check it out for yourself if you want to learn more about it or even use it in your projects. Good luck!
Comments [1] | | # 
Thursday, April 10, 2008 9:36:50 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Other | Programming | Reviews | XNA )
This article is very similar to the original "Skeletal Bone Animation and Skinning with Collada Models in XNA", which is one of the most read articles on my blog. The old article and the SkinningWithColladaModelsInXna project did only work on XNA 1.0, this is the updated version for XNA 2.0. Most of the content of this post stay the same, but the source code and downloads have changed now.

Content


Video, Screenshot and Downloads

First of all, here is a video for the project I'm talking about here:

Screenshot of the same scene (better quality since youtube videos just suck quality wise):

Before you start reading this article I suggest you download the executable and source code first in case you want to try it out directly while reading. The whole project is based on the RocketCommanderXNA engine and just adds the ColladaModel.cs class, which does all the amazing stuff you see in the video ;-) The video above shows shadowing from the XNA Shooter engine, which is not yet released and their fore not included in the downloads. The rest of the code is the same, just the shadow map rendering was removed. More details about shadow mapping in XNA can also be found in my book (plug plug plug ^^).

  • The source code is about 2.6 MB and contains projects for XNA Game Studio (which works in Visual Studio 2005 too thanks to XNA 2.0). It was tested on Windows XP and Vista 32 and 64 bit as well as on the Xbox 360. Please not the PostScreenShaders are untested and need some tweaking to work correctly, but all the other things like the model loading and rendering work just fine.
    XnaSkinningColladaModelsSourceCode.zip (2.6 MB)
  • The executable is available too, it will install all necessary files like the XNA 2.0 Framework and DirectX files. You will however need a Shader Model 2.0 capable graphic card. You can read the next blog post for more information on how to create such setups :)
    XnaSkinningColladaModelsSetup.exe (2.2 MB)


Introduction and why Collada?

Note: The rest of this article is from Feb 2007, I have just corrected some errors and updated the source code.

OK, let's get started. I talked a lot about the XNA Content Pipeline and its problems on my blog, and in several recent interviews and also in my upcoming XNA book. For projects like Rocket Commander or even the Racing Game it was sometimes a little bit annoying, but I could do everything I needed by loading X files and adding some features to it to fix the tangents, load the correct shader techniques, etc.
However XNA does not support loading animation data or gives you a way to display them. You have to do all that work by yourself. This includes static mesh animations (like it was used in Rocket Commander, I just left it out in Rocket Commander XNA), but also skeletal animation with bones and skinned meshes.

There is a nice project on CodePlex called XNA Animation Component Library, but it only support FBX and ASCII X files (and recently BVH, ASF and AMC formats) and I could not get any of my test models to work with this library. The main thing missing here is good shader support and it also has a lot of problems with complex animated models. Other than that exporting 3D Models as X files is really a pain in the ass, no matter which exporter you use (Panda Exporter for 3dsMax was good a year ago, I still use it, now kiloWatt X file Exporter for 3dsMax is better, and the Microsoft X File Exporter for Max always sucked). Simple models might work, but the more complex 3D models get and the more meshes and animation data is stored in a scene, the more problems you will have. Sometimes it is not possible to reconstruct everything correctly on your importer side.

Anyway, at the very beginning of the Racing Game development there was no content pipeline in XNA (it was Beta 1) and I implemented loading 3D Models with help of the Collada file format, which is basically just XML and very easy to read. For that reason it was relatively simple to get some 3D data loaded and displayed in the early XNA versions with help of vertex buffers. There were some problems with shader settings and I had to try many different exporters and ended up with the one from Feeling Software. Back then it had still some problems loading shaders and using the correct techniques, but the recent version (3.0.2) is much better and works like a charm.

After Microsoft had implemented the content pipeline and made it possible to load X and FBX files indirectly by going over the content pipeline I had to remove most of my Collada code and re implement the model loading with the new framework. Loading and displaying 3D models was much simpler this way and especially some early unit tests were really simple, but as soon I tried shaders and loading tangents there were a bunch of problems. I reported many bugs back then and it has gotten a lot better, but I still had to fix several issues in the Racing Game and Rocket Commander XNA myself like finding out the correct shader technique and fixing tangent data with help of a custom model processor.

Some things like the level loading in the Racing Game just do not work with X or FBX files because they use splines, which are not exported at all in these formats. Collada came as the rescue again because it is really no big problem for this format. Later versions of the Racing Game removed the Collada level loading and introduced a binary format for the levels, but the importer still accepts Collada files.

Recently I wanted to test a couple of animated models and use skinning since it can often be more useful than static animations and it usually looks much better, especially for organic 3D models. As I said above I could not get anything working with the XNA Animation Component Library and I especially do not like the way they still use the content pipeline and the project is too complicated for me anyway (I just do not need 6 different file formats, I just want one and it should work perfectly with all the features I need).

After some searching I saw some guy called remi from the Collada forum was working on importing Collada models too and I posted some thoughts there too (maybe this was a mistake, I got many emails asking me about tips ^^). Here is the thread about that in the Collada forum. He has provided a test project with some models and it works nice for static meshes without shader information, but that was not really what I was searching for.

This was a month ago and I had not much time working on any of these issues, but after restarting my blog earlier this week I thought this would be a nice topic to talk about. I'm still pretty busy with Arena Wars Reloaded, but I worked a couple of hours every day on this little test program for the past few days. The rest of this article explains the project and into which problems I ran.

Class Overview

Before we go into the details here is a little class overview of the project. As I said before the Rocket Commander XNA engine was used to get up and running with the project without having to re implement the basics. Most of the classes were already in place and had not to be changed. ColladaModel and SkinnedTangentVertex are the 2 new classes and to help us out with the XML loading of the Collada files the XmlHelper class was also brought into the project.

I wrote the ColladaModel file from scratch, but I could reuse some of the static mesh loading code I had done last year. All of the bone and animation loading code was just try and error and I only used the Collada specifications as a source of help, but most stuff had to be tested with the unit tests at the end of the class many many times.

The main program just calls the unit tests TestGoblinColladaModelScene or TestShowBones, there is no real game here, its just a test program. The unit test then calls several Standard Engine classes for doing all the post screen processing, rendering the ground plane, etc. More importantly the ColladaModel class itself basically just provides a constructor and a Render method, everything else is private and will be handled automatically for you. Most people might ignore this, but this is always the most important thing about my classes, the use should be as simple as possible and when I look at the projects mentioned above I really ask myself why people sometimes think so complicated.

The internal Bone class inside ColladaModel is used to store all the bones in a flat list, but each entry has a parent and a list old children bones. This way the list can be used both in a simple for loop, but you can also go through it recursively (which is obviously slower and often more complicated). We will talk about the loading process in a minute.

All the mesh data is stored in vertices, which is just a list of SkinnedTangentVertex structs. The SkinnedTangentVertex struct is very much like the standard TangentVertex struct used in Rocket Commander XNA, but it has 2 new members: blendIndices and blendWeights. Both are in the form of Vector3 and their fore can hold 3 values allowing us to interpolate up to 3 bone influences for each vertex in the shader. More is often not required and we have to re normalize all bone weights anyway, so skipping the least important bone weights is not a big deal. My test models use mostly max. 2-3 influences. Please also note that the vertex shader has now a lot more work to do with all that skinning and you should really optimize it as much as possible. Both the number of vertices we have to process is important (we will talk about optimizing that in the optimizing part of this article) and also the number of instructions the vertex shader has, both numbers should be as low as possible. The GPU is really fast processing this data, but if you do not have animated geometry with bones, there is no reason to let it process all that data (which can make the vertex shader 2-3 times longer and slower). The ground of our test scene does not use a skinning shader as an example.

And finally there are some additions to the ShaderEffect class. First of all we got a new shader called "SkinnedNormalMapping", which does the same thing as the normal mapping (or parallax mapping) shader, but it has an array of 80 bone matrices we can use for skinning. These matrices are set with help of the SetBoneMatrices method in the Set Parameter region of ShaderEffect.cs.


Loading Collada files

Before we go into the details of the loading process, lets make sure we read the summary of the class first because it clearly states what we can do and can't do with this class. This is just a test project and I wanted to make things as simple as possible for both you as the reader and for my requirements.

   /// <summary>
   /// Collada model. Supports bones and animation for Collada (.dae) exported
   /// 3D Models from 3D Studio Max (8 or 9).
   /// This class is just for testing and it will only display one single mesh
   /// with bone and skinning support, the mesh also can only have one single
   /// material. Bones can be either in matrix mode or stored with transform
   /// and rotation values. SkinnedTangentVertex is used to store the vertices.
   /// </summary>
   class ColladaModel : IDisposable

OK, with that said let's go directly into the loading code, which is located in the constructor of this class. All variables used in these class are just for internal use, all you need to know are the vertices and bone lists, which I have already mentioned, and the vertex and index buffers, which are used for rendering. All the rest of the variables are just there to help us loading the Collada file (don't worry, there are not many variables anyway and most methods are short too).

    #region Constructor
    /// <summary>
    /// Create a model from a Collada file
    /// </summary>
    /// <param name="setName">Set name</param>
    public ColladaModel(string setName)
    {
        // Set name to identify this model and build the filename
        name = setName;
        string filename = Path.Combine(ColladaDirectory,
            StringHelper.ExtractFilename(name, true) + "." +
            ColladaExtension);

        // Load file
        Stream file = File.OpenRead(filename);
        string colladaXml = new StreamReader(file).ReadToEnd();
        XmlNode colladaFile = XmlHelper.LoadXmlFromText(colladaXml);

        // Load material (we only support one)
        LoadMaterial(colladaFile);
        
        // Load bones
        LoadBones(colladaFile);

        // Load mesh (vertices data, combine with bone weights)
        LoadMesh(colladaFile);

        // And finally load bone animation data
        LoadAnimation(colladaFile);

        // Close file, we are done.
        file.Close();
    } // ColladaModel(setFilename)
    #endregion

As you can see first of all the filename is constructed and we just load the file as a text file and throw it to the XmlHelper.LoadXmlFromText helper method (which just uses the existing XmlDocument functionality to load XML from a string). We now get the main Collada node, which contains all the children nodes we need for loading the materials, bones, meshes, etc.

Next all the materials are loaded, but we are only going to use the first one we find because we only support one single mesh anyway. The LoadMaterial method goes through all used textures and shader effects from the Collada file and constructs the material at the end of the method with help of a new constructor in the Material class itself. While this is cool and a lot easier than loading material data from x files, it is not very exciting code, so let's move along.

Even through the bones are located at the end of the Collada file, we have to load them first because all our other loading methods, specifically LoadMesh and LoadAnimation need the bone tree structure and the overall bone list to work. All bones are loaded in sequential order because we want to make sure that we can use the animation matrices later in an easy way without having to check the parent order all the time. Only this way we can be sure that going through our flat bones list we still respect the internal tree structure and always initialize the parents first because the children bones matrices are always multiplied with the parent bones.

    foreach (XmlNode boneNode in boneNodes)
        if (boneNode.Name == "node" &&
            (XmlHelper.GetXmlAttribute(boneNode, "id").Contains("Bone") ||
            XmlHelper.GetXmlAttribute(boneNode, "type").Contains("JOINT")))
        {
            // [...] get matrix
            matrix = LoadColladaMatrix(...);

            // Create this node, use the current number of bones as number.
            Bone newBone = new Bone(matrix, parentBone, bones.Count,
                XmlHelper.GetXmlAttribute(boneNode, "sid"));

            // Add to our global bones list
            bones.Add(newBone);
            // And to our parent, this way we have a tree and a flat list in
            // the bones list :)
            if (parentBone != null)
                parentBone.children.Add(newBone);

            // Create all children (will do nothing if there are no sub bones)
            FillBoneNodes(newBone, boneNode);
        } // foreach if (boneNode.Name)

As you can see the code uses the XmlHelper class extensively because otherwise the code would look much uglier and complex. Next we have to load the mesh itself, this is probably the longest method and not easy to figure out if you work with Collada for the first time. Good thing I had already done that in the past and I only had to add the code for getting the blend weights and indices. The following code does load all the weights, which we will use later to fill the blendWeights and blendIndices members of the SkinnedTangentVertex struct vertices list. The code for that is actually a little bit more complicated because we have to find out which weights are the top 3 weights for each vertex in case more than 3 are given.


    #region Load weights
    float[] weights = null;
    foreach (XmlNode sourceNode in skinNode)
    {
        // Get all inv bone skin matrices
        if (sourceNode.Name == "source" &&
            XmlHelper.GetXmlAttribute(sourceNode, "id").Contains("bind_poses"))
        {
            // Get inner float array
            float[] mat = StringHelper.ConvertStringToFloatArray(
                XmlHelper.GetChildNode(sourceNode, "float_array").InnerText);
            for (int boneNum = 0; boneNum < bones.Count; boneNum++)
                if (mat.Length / 16 > boneNum)
                {
                    bones[boneArrayOrder[boneNum]].invBoneSkinMatrix =
                        LoadColladaMatrix(mat, boneNum * 16);
                } // for if
        } // if

        // Get all weights
        if (sourceNode.Name == "source" &&
            XmlHelper.GetXmlAttribute(sourceNode, "id").Contains("skin-weights"))
        {
            // Get inner float array
            weights = StringHelper.ConvertStringToFloatArray(
                XmlHelper.GetChildNode(sourceNode, "float_array").InnerText);
        } // if
    } // foreach

    if (weights == null)
        throw new InvalidOperationException(
            "No weights were found in our skin, unable to continue!");
    #endregion

For more information about the mesh loading please check out the last region in the LoadMesh method, it should explain all the important steps in case you want to add something there or just look how it works.

Problems with the animation data

Getting the animation data was not so easy. First of all I never had done this before because my Collada files for the Racing Game were all just static meshes and I really did not need any animation there. Everything that is actually animated in the Racing Game was done directly in XNA, not in 3D Studio.

The first problem is the many formats that animation data can be in. You can have rotations around any axis or translations and even scalings, but most of your bones will only use one or two of these if they are animated at all. Alternatively all the animation data can be computed directly by the exporter plugin in 3D Studio Max and this way you can make sure that all the animation data is in the correct format. It makes testing certainly a lot harder and if you don't even know the format the matrices are in or how to apply them to each other in which order, you are in a world of trouble.

This is exactly what happened to me, I had most of my test models with rotation animation data only, but the Goblin above from my friend Christoph was done with another technique and the exporter could only export the matrices, so I had to support that too. After some try and error I managed to get the basic animations for my test models working. They are all in the project, feel free to load and test them. To test the bone animations I used the following unit test:

    #region Unit Testing
    // Note: Allow calling all this even in release mode (see Program.cs)
    #region TestShowBones
    /// <summary>
    /// TestShowBones
    /// </summary>
    public static void TestShowBones()
    {
        ColladaModel model = null;
        PlaneRenderer groundPlane = null;
        // Bone colors for displaying bone lines.
        Color[] BoneColors = new Color[]
            { Color.Blue, Color.Red, Color.Yellow, Color.White, Color.Teal,
            Color.RosyBrown, Color.Orange, Color.Olive, Color.Maroon, Color.Lime,
            Color.LightBlue, Color.LightGreen, Color.Lavender, Color.Green,
            Color.Firebrick, Color.DarkKhaki, Color.BlueViolet, Color.Beige };

        TestGame.Start("TestLoadColladaModel",
            delegate
            {
                // Load our goblin here, you can also load one of my test models!
                model = new ColladaModel(
                    //"Goblin");
                    //"test_bones_simple_baked");
                    //"test_bones_advanced_baked");
                    "test_man_baked");

                // And load ground plane
                groundPlane = new PlaneRenderer(
                    new Vector3(0, 0, -0.001f),
                    new Plane(new Vector3(0, 0, 1), 0),
                    new Material(
                        "GroundStone", "GroundStoneNormal", "GroundStoneHeight"),
                    50);
            },
            delegate
            {
                // Show ground
                groundPlane.Render(ShaderEffect.parallaxMapping, "DiffuseSpecular20");

                // Show bones without rendering the model itself
                if (model.bones.Count == 0)
                    return;

                // Update bone animation.
                model.UpdateAnimation(Matrix.Identity);

                // Show bones (all endpoints)
                foreach (Bone bone in model.bones)
                {
                    foreach (Bone childBone in bone.children)
                        BaseGame.DrawLine(
                            bone.finalMatrix.Translation,
                            childBone.finalMatrix.Translation,
                            BoneColors[bone.num % BoneColors.Length]);
                } // foreach (bone)
            });
    } // TestShowBones()
    #endregion

The most important call here is the call to UpdateAnimations, which goes through the list of bones and updates the so called finalMatrix from the Bone class for each of the bones. In earlier versions this code was horribly complicated and still had a lot of problems, but as soon as I removed all the rotation, translation, scaling, etc. animation support and just allow loading the correctly baked matrices from the Collada files, the code has become much simpler (the actual code does have some optimizations in it, but it is basically the same as the posted code here):

    #region Update animation
    /// <summary>
    /// Update animation.
    /// </summary>
    private void UpdateAnimation(Matrix renderMatrix)
    {
        int aniMatrixNum = (int)(BaseGame.TotalTime * frameRate)) % numOfAnimations;

        foreach (Bone bone in bones)
        {
            // Just assign the final matrix from the animation matrices.
            bone.finalMatrix = bone.animationMatrices[aniMatrixNum];

            // Also use parent matrix if we got one
            // This will always work because all the bones are in order.
            if (bone.parent != null)
                bone.finalMatrix *=
                    bone.parent.finalMatrix;
        } // foreach
    } // UpdateAnimation()
    #endregion

For the loading itself we just have to make sure that the animationMatrices are the correct ones. Collada saves them in a absolute mode. Earlier code from me constructed relative matrices (relative to the initial bone matrix), it was easier to construct relative matrices from the rotation, translation, etc. animation data, but much harder to use these matrices later for the animation. Having these absolute matrices makes the UpdateAnimation code so much easier, so make sure you always use them this way.

However, when rendering the vertices later we can't use the absolute matrices because the vertices have to transformed first to get into a relative space to the bones, rotations should not be around the origin, but around the bone positions. Luckily for us (and you should have seen my face when I finally found out that these matrices already exist in Collada and I did not have to create them myself in my own over complicated way ^^) Collada stores the so called invBoneSkin matrices for each bone. By applying these matrices we can easily get the bone matrices we need for rendering, these are directly passed to our shader (as compressed 4x3 matrices BTW to save shader constants, the code for that is a little bit more complex, please check out ShaderEffect.cs and the SkinnedNormalMapping.fx shader itself for details).


    #region GetBoneMatrices
    /// <summary>
    /// Get bone matrices for the shader. We have to apply the invBoneSkinMatrix
    /// to each final matrix, which is the recursively created matrix from
    /// all the animation data (see UpdateAnimation).
    /// </summary>
    /// <returns></returns>
    private Matrix[] GetBoneMatrices(Matrix renderMatrix)
    {
        // Update the animation data in case it is not up to date anymore.
        UpdateAnimation(renderMatrix);

        // And get all bone matrices, we support max. 80 (see shader).
        Matrix[] matrices = new Matrix[Math.Min(80, bones.Count)];
        for (int num = 0; num < matrices.Length; num++)
            // The matrices are constructed from the invBoneSkinMatrix and
            // the finalMatrix, which holds the recursively added animation matrices
            // and finally we add the render matrix too here.
            matrices[num] =
                bones[num].invBoneSkinMatrix * bones[num].finalMatrix * renderMatrix;

        return matrices;
    } // GetBoneMatrices()
    #endregion

And this is what you finally get after executing the TestShowBones unit test. I had not implemented mesh loading or the shader itself at this point. I just was loading and testing the bones itself.

 

Optimizing the vertices

One major problem with the loaded mesh is the high vertices count, I had two test models with 30k and 60k vertices and as you can imagine this will slow down the vertex shader quite a lot and it is really not necessary to process all these vertices because many of them are exactly the same. The reason we end up with an unoptimized vertices list anyway is because Collada stores separate lists for each component we have to put together at the end of the LoadMesh method. By doing so we have to duplicate the data many times and we just can't know how often each part is reused and how often the overall vertex changes. If just the texture coordinate or normal differs, we have a completely different vertex, which will produce different results in the vertex shader, so just merging everything together is not that simple.

The rendering uses an index buffer anyway, but for the data constructed in LoadMesh it would just be sequential (0, 1, 2 form the first polygon, 3, 4, 5 the next, etc.). Instead of having one index for each vertex, we can often reuse the same vertex 3 or 4 times and reducing the number of vertices drastically. This has also the advantage that we can store much more vertices and complexer meshes even if we still use ushort (16 bit) for our indices (which is half the size of ints and their fore faster). For example if you have 150,000 vertices, but you can reduce them to 40-50,000 optimized vertices, they all can be indexed with ushorts :-)

The easy solution is just to optimize all the vertices after all of them have been loaded, if you have a binary format and do not use Collada directly, this solution is absolutely great, but it still will take a lot of time processing the Collada models if they just have many vertices because we have to check each vertex against each other one and that can be a lot of compares if you have 60 or 70 thousand vertices in a mesh. It actually takes up to a whole minute just to compute that and I have no slow computer ^^ Here is the method that does all that for us:

    #region OptimizeVertexBufferSlow
    /// <summary>
    /// Optimize vertex buffer. Note: The vertices list array will be changed
    /// and shorted quite a lot here. We are also going to create the indices
    /// for the index buffer here (we don't have them yet, they are just
    /// sequential from the loading process above).
    ///
    /// Note: Slow version because we have to check each vertex against
    /// each other vertex, which makes this method exponentially slower
    /// the more vertices we have. Takes 10 seconds for 30k vertices,
    /// and over 40 seconds for 60k vertices. It is much easier to understand,
    /// but it produces the same output as the fast OptimizeVertexBuffer
    /// method and you should always use that one (it only requires a couple
    /// of milliseconds instead of the many seconds this method will spend).
    /// </summary>
    /// <returns>ushort array for the optimized indices</returns>
    private ushort[] OptimizeVertexBufferSlow()
    {
        List<SkinnedTangentVertex> newVertices =
            new List<SkinnedTangentVertex>();
        List<ushort> newIndices = new List<ushort>();

        // Go over all vertices (indices are currently 1:1 with the vertices)
        for (int num = 0; num < vertices.Count; num++)
        {
            // Try to find already existing vertex in newVertices list that
            // matches the vertex of the current index.
            SkinnedTangentVertex currentVertex = vertices[FlipIndexOrder(num)];
            bool reusedExistingVertex = false;
            for (int checkNum = 0; checkNum < newVertices.Count; checkNum++)
            {
                if (SkinnedTangentVertex.NearlyEquals(
                    currentVertex, newVertices[checkNum]))
                {
                    // Reuse the existing vertex, don't add it again, just
                    // add another index for it!
                    newIndices.Add((ushort)checkNum);
                    reusedExistingVertex = true;
                    break;
                } // if (TangentVertex.NearlyEquals)
            } // for (checkNum)

            if (reusedExistingVertex == false)
            {
                // Add the currentVertex and set it as the current index
                newIndices.Add((ushort)newVertices.Count);
                newVertices.Add(currentVertex);
            } // if (reusedExistingVertex)
        } // for (num)

        // Reassign the vertices, we might have deleted some duplicates!
        vertices = newVertices;

        // And return index list for the caller
        return newIndices.ToArray();
    } // OptimizeVertexBufferSlow()
    #endregion

 

Optimizing the Optimization

While this is all nice and dandy and we just optimized the rendering code by 20-30% (I tested with 9 goblins and using 3 passes for them, 2 for shadowing and 1 for the rendering), but the loading now takes painfully long. The main idea here is to not compare every single vertex against every other possible vertex because it does not make much sense, most of the vertices (>99,9%) will always be different. We only need to check the ones that share at least the same position.

I started with comparing neighboring vertices, but since the vertices are stored in index order, they are totally messed up, vertex 1 and 4383 can be equal, but if we just check -10 to +10 we are going to miss it. Instead we have to know which vertices come from the same position data, which we know since Collada saves unique positions. All we have to do is to keep a list of all vertices that share the same position and then we can optimize the comparisions later on. Usually only up to 4 to 6 vertices share the same position, this way the whole comparison process just needs 60,000 * 6 comparisons, not 60,000*60,000 anymore.

    // Initialize reuseVertexPositions and reverseReuseVertexPositions
    // to make it easier to use them below
    reuseVertexPositions = new int[trianglecount * 3];
    reverseReuseVertexPositions = new List<int>[positions.Count / 3];
    for (int i = 0; i < reverseReuseVertexPositions.Length; i++)
        reverseReuseVertexPositions[i] = new List<int>();

    // We have to use int indices here because we often have models
    // with more than 64k triangles (even if that gets optimized later).
    for (int i = 0; i < trianglecount * 3; i++)
    {
        // [...] vertex construction

        // Remember pos for optimizing the vertices later more easily.
        reuseVertexPositions[i] = pos / 3;
        reverseReuseVertexPositions[pos / 3].Add(i);
    } // for (ushort)

And then finally the fast version of OptimizeVertexBuffer, which uses that data:

    #region OptimizeVertexBuffer
    /// <summary>
    /// Optimize vertex buffer. Note: The vertices list array will be changed
    /// and shorted quite a lot here. We are also going to create the indices
    /// for the index buffer here (we don't have them yet, they are just
    /// sequential from the loading process above).
    ///
    /// Note: This method is highly optimized for speed, it performs
    /// hundred of times faster than OptimizeVertexBufferSlow, see below!
    /// </summary>
    /// <returns>ushort array for the optimized indices</returns>
    private ushort[] OptimizeVertexBuffer()
    {
        List<SkinnedTangentVertex> newVertices =
            new List<SkinnedTangentVertex>();
        List<ushort> newIndices = new List<ushort>();

        // Helper to only search already added newVertices and for checking the
        // old position indices by transforming them into newVertices indices.
        List<int> newVerticesPositions = new List<int>();

        // Go over all vertices (indices are currently 1:1 with the vertices)
        for (int num = 0; num < vertices.Count; num++)
        {
            // Get current vertex
            SkinnedTangentVertex currentVertex = vertices[num];
            bool reusedExistingVertex = false;

            // Find out which position index was used, then we can compare
            // all other vertices that share this position. They will not
            // all be equal, but some of them can be merged.
            int sharedPos = reuseVertexPositions[num];
            foreach (int otherVertexIndex in reverseReuseVertexPositions[sharedPos])
            {
                // Only check the indices that have already been added!
                if (otherVertexIndex != num &&
                    // Make sure we already are that far in our new index list
                    otherVertexIndex < newIndices.Count &&
                    // And make sure this index has been added to newVertices yet!
                    newIndices[otherVertexIndex] < newVertices.Count &&
                    // Then finally compare vertices (this call is slow, but thanks to
                    // all the other optimizations we don't have to call it that often)
                    SkinnedTangentVertex.NearlyEquals(
                    currentVertex, newVertices[newIndices[otherVertexIndex]]))
                {
                    // Reuse the existing vertex, don't add it again, just
                    // add another index for it!
                    newIndices.Add((ushort)newIndices[otherVertexIndex]);
                    reusedExistingVertex = true;
                    break;
                } // if (TangentVertex.NearlyEquals)
            } // foreach (otherVertexIndex)

            if (reusedExistingVertex == false)
            {
                // Add the currentVertex and set it as the current index
                newIndices.Add((ushort)newVertices.Count);
                newVertices.Add(currentVertex);
            } // if (reusedExistingVertex)
        } // for (num)

        // Finally flip order of all triangles to allow us rendering
        // with CullCounterClockwiseFace (default for XNA) because all the data
        // is in CullClockwiseFace format right now!
        for (int num = 0; num < newIndices.Count / 3; num++)
        {
            ushort swap = newIndices[num * 3 + 1];
            newIndices[num * 3 + 1] = newIndices[num * 3 + 2];
            newIndices[num * 3 + 2] = swap;
        } // for

        // Reassign the vertices, we might have deleted some duplicates!
        vertices = newVertices;

        // And return index list for the caller
        return newIndices.ToArray();
    } // OptimizeVertexBuffer()
    #endregion

With this optimization loading is now pretty fast and rendering performs also nicely thanks to the quick shaders we will discuss below. When running ANTS Profiler over the new project the slowest line of code becomes the actual text parsing, actually the conversion of the long strings for all vertices data into the actual vertices float data, especially the positions array. But we can't do anything about that without loading the data binary and not parsing them ourselves. It takes maybe half a second for a 50k vertices model to load, not great, but ok for our little test app.

Adjusting the shaders for skinned meshes

Now we got a bunch of vertices we can render, but as you know we need a shader to do anything in XNA, there is no fixed function pipeline. There are also no animation helper classes like in DirectX, but they won't help you anyway if you want to render with shaders. Transforming the vertices on the CPU may sometimes be a choice if you do not have many vertices and not many skinned models overall or if they all have to be updated the same way. But generally it is a much better idea to transform all the vertices on the GPU, which costs a little bit more instructions in the vertex shader, but the rest of the rendering stays the same. Most graphic apps are pixel shader bound anyway and I use fairly complex pixel shaders too. Additionally on Shader Model 4.0 cards like the GeForce 8800 with up to 128 parallel unified shader units you can do very complex vertex shaders and use simpler pixel shaders and it will just use more units for the vertex shaders automatically :)

We already have defined the bones matrix array for skinning above and we limited it to 80 bones per mesh, which is quite a lot. Even if you would spend 3 bones per finger and 20-30 bones for the body you would still have plenty of bones left for complex animations. Sure modelers will now say "thats not enough sometimes" ... well, you can always split up the mesh into several meshes with up to 80 bones each if you really need more. My graphic artists are happy with 80 bones ^^

If we want full Shader Model 2.0 support we can only be sure that we got at least 256 shader constants. NVidia has usually more (1024), but you still want your game to run on ATI cards too, especially older ones. Each constant can hold a float4 and we need 4 constants for each 4x4 matrix. This means we can only have up to 64 matrices with this limit and we still need some constants for the world, view, and projection matrices, the light and material values and anything else we want to do in our shader. You should reserve 10-20 constants for that and now we are down to less than 60 matrices, which might sometimes be too little.

Instead of splitting the mesh or providing a different code path in case the GPU can do more constants (I couldn't get that do work in XNA for some reason, not sure if there is some limit or if I made a mistake, my GPU should be able to do at least 1024 constants, and even 2048 for the 8800), there is a trick to save only a 3x4 matrix. The last column is always 0, 0, 0, 1 if we have correctly applied the invBoneMatrix and the animation matrix (see GetBoneMatrices in ColladaModel for details and the order of the matrices). But saving 4 float3 values still needs 4 shader constants per matrix so we have to save it as 4x3 matrices instead.

My first idea was to grab the .w values and reconstruct the translation part of the matrix this way. This worked, but the resulting shader had 80 instructions (from about 20 without skinning), which is not good for the resulting performance when rendering many skinned 3D models.

float4x4 RebuildSkinMatrix(float index)
{
    return float4x4(
        float4(skinnedMatricesVS20[index*3+0].xyz, 0),
        float4(skinnedMatricesVS20[index*3+1].xyz, 0),
        float4(skinnedMatricesVS20[index*3+2].xyz, 0),
        float4(
            skinnedMatricesVS20[index*3+0].w,
            skinnedMatricesVS20[index*3+1].w,
            skinnedMatricesVS20[index*3+2].w, 1));
} // RebuildSkinMatrix(.)

A better idea is to use the 4x3 matrix as a 3x4 matrix by just reversing the order we call mul. This involves some changes to the vertex shader code and looks a little bit confusing sometimes, but if you make sure you transform the world matrix the same way and use it in this reversed mul order too, everything works just fine. Another thing that can be optimized is to pre-multiply the indices of blendIndices for each vertex at the loading time. These indices never change and they don't really care if they are 0, 1, 2 or 0, 3, 6, etc. We save one instruction per matrix we are going to reconstruct (the rest is optimized out by the compiler). This is the much easier version of RebuildSkinMatrix, for more details take a look at the SkinnedNormalMapping.fx file:

// Note: This returns a transposed matrix, use it in reversed order.
// First tests used a 3x3 matrix +3 w values for the transpose values, but
// reconstructing this in the shader costs 20+ extra instructions and after
// some testing I found out this is finally the best way to use 4x3 matrices
// for skinning :)
float4x4 RebuildSkinMatrix(float index)
{
    return float4x4(
        skinnedMatricesVS20[index+0],
        skinnedMatricesVS20[index+1],
        skinnedMatricesVS20[index+2],
        float4(0, 0, 0, 1));
} // RebuildSkinMatrix(.)

This results in a vertex shader that has almost half as many instructions as before and their fore is twice as fast :) Good work. Performance tests showed that I could increase the framerate from 220 fps to 270 fps just by doing that (test scene with 9 goblins). The following code is actually more or less the only part you have to replace in an existing shader if you want to make it skinnable (plus providing the helper method and the skinned matrices too of course).


    // First transform position with bones that affect this vertex
    // Use the 3 indices and blend weights we have precalculated.
    float4x4 skinMatrix =
        RebuildSkinMatrix(In.blendIndices.x) * In.blendWeights.x +
        RebuildSkinMatrix(In.blendIndices.y) * In.blendWeights.y +
        RebuildSkinMatrix(In.blendIndices.z) * In.blendWeights.z;
    
    // Calculate local world matrix with help of the skinning matrix
    float4x4 localWorld = mul(world, skinMatrix);
    
    // Now calculate final screen position with world and viewProj matrices.
    float4 worldPos = mul(localWorld, float4(In.pos, 1));
    Out.pos = mul(worldPos, viewProj);

 

Post screen shaders for the final result

All the skinning code and bone transformations are nice once you get them done, but without a cool model and some post screen shaders to make the scene look more cool, it is only half the fun. Good think I got the Goblin 3D Model, thanks to Christoph (WAII) again. I also had a couple of other test models and another more complex 3D Model (big evil monster ^^) which was good for some stress testing, but the material just did not look that cool.

As you can see on the following image 6 render targets are used to accomplish the final image. Most shader passes do several things at once and the list of operations (see right side of the image) is longer than the list of used passes. Most of this was just trying to get the best looking values together quickly. If you are an experienced artist you can probably do much better than me, I'm just playing around with the shader parameters until I get bored and then I leave it the way it is. The sceneMap (render target number three) shows the unmodified scene without applying the post screen shaders. It does not look half as cool as the final result.

I hope you enjoyed this article and that you are not as tired as I am writing this all at once (uff). It was a fun project, I have already another one in mind for next week ;-) Take care. If you have problems, post a comment. Please note that not all Collada models will work, you have to follow the rules of ColladaModel or improve the class a bit for other use

 

References and Links

 

 

Comments [0] | | # 
 Wednesday, April 09, 2008
Wednesday, April 09, 2008 10:11:37 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | Racing Game | Reviews | XNA )
This article is similar to the last one (How to export .X files from 3DS Max for use in XNA), but this time we are going to export a 3D Model from 3DS Max as a Collada (.dae) file. Most tips are similar, just the output file (Cactus.dae) is different and the engine we will be using is DungeonQuestGDC or the "Skeletal Bone Animation and Skinning with Collada Models in XNA" engine (I will write another article about that in a bit), which support Collada model files.

As I just said, parts of this article will be similar to the last one: First of all you (or your graphic artist) need a version of 3DS Max, I recommend the most recent available version (e.g. Max 2008 has much better support for .DDS files and makes the exporting process way easier). If you do not have 3DS Max, you can test it for 30 days, that should be enough for testing and exporting some existing 3D Models ^^

You will need the ColladaMax Exporter for your Max version, you can find all versions and downloads at the http://www.feelingsoftware.com/ website, more specific information can be found at: http://www.feelingsoftware.com/content/view/65/79/lang,en/. You can register on the site and then download the ColladaMax plugin for 3DS Max for free there.

Similar as discussed in the last article you have to make sure that each mesh that you export has a shader material assigned to it and in order to work correctly with your XNA Graphic engine it must be a shader that is supported by the engine like ParallaxMapping.fx, NormalMapping.fx or some other test shader.

Lets go through all the steps. Please note that experienced 3DS Max users will probably only need the ColladaMax Exporter and figure the rest out themselves. This tutorial is for beginners and people having trouble getting 3D Models exported for the XNA Graphic Engine in DungeonQuestGDC or SkinningColladaModelsInXna (or similar engines). See http://XnaProjects.net for more details and downloads of these projects!

Step 1: Load the Cactus.max (312 KB) model as an example. If you put the used texture Cactus.dds (170.8 KB) in the same directory you should see the following result:


Step 2: In case you can't see the model, also make sure the used Shader (NormalMapping.fx (10.99 KB, for 3ds Max) for this model, make sure you have a NormalMapping.fx in your XNA Graphic engine) and all other required files exist in the same directory; like the normal map texture CactusNormal.dds (341.47 KB).
If you want to change an existing 3D model and let it use a shader, open up the Material Editor in 3DS Max (e.g. by using the M hotkey) and create a new DirectX shader material. To do that click on a free material spot, click Standard (1.) and then select DirectX Shader (2.), now select the shader you want to use and fill in all the required shader parameters (colors, textures, etc.).


Step 3: Now export the 3D Model as an Collada (.dae) file via the ColladaMax Exporter mentioned above. Make sure settings are similar as follows, basically just activate everything. In case you have animation data in your 3D Model you can also activate the Export Animation checkbox. Multiple animations or the so called Animation Clips do not work very well, so I usually export all the frames and then add some extra animation tracks into an extra .xml file. Anyway, our model does not have any animation and in this case it is much easier both to export and to test.


Step 4: Now you should have the Cactus.DAE (38.95 KB) model file. Unlike previous projects I do not use the XNA Content pipeline for Collada models, I just create a /Models directory and put all the models in there. The textures are stored in /Textures/Models and finally the Shaders go in the /Shaders directory. Thanks to the fact that we do not use the XNA Content pipeline we can keep the game application running while exchanging 3D models and loading them again and again to make improvements, which is cool :)

Step 5: Now with everything in place it is time to test if we can load the model and see it on the screen. I usually use the following unit test in the Model.cs class called TestLoadModel:



Step 6: And finally we should see something like this (this test is from the DungeonQuestGDC engine from http://XnaProjects.net):


I hope this helps.
Comments [1] | | # 
Tuesday, April 08, 2008 11:28:03 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | Racing Game | XNA )
This little article give tips on how to export an .X file from 3DS Max 8/9/2008 into the XNA Racing Game engine. I got several emails on how to do that and instead of repeating myself, here is the article :)

First of all you (or your graphic artist) need a version of 3DS Max, I recommend the most recent available version (e.g. Max 2008 has much better support for .DDS files and makes the exporting process way easier). If you do not have 3DS Max, you can test it for 30 days, that should be enough for testing and exporting some existing 3D Models ^^

Ok, next you will need the Pandasoft DirectX Exporter for your Max version, you can find all versions and downloads here. Also make sure you meet all the prerequisites:
http://www.andytather.co.uk/Panda/directxmax_downloads.aspx

Before you start exporting you have to understand that XNA can only render 3D geometry when a shader is used. You can't just put up a gray box like in 3DS Max that easily (if you would like to do that you will need a simple gray box shader). For this reason you will have to make sure that each mesh that you export has a shader material assigned to it and in order to work correctly with the XNA Racing Game engine it must be a shader that is supported by the engine like ParallaxMapping.fx, NormalMapping.fx or some other test shader.

Lets go through all the steps. Please note that experienced 3DS Max users will probably only need the Panda Exporter and figure the rest out themselves. This tutorial is for beginners and people having trouble getting 3D Models exported for the XNA Racing Game (or other games that used my XNA Graphic Engine project).

Step 1: Load the Cactus.max (312 KB) model as an example. If you put the used texture Cactus.dds (170.8 KB) in the same directory you should see the following result:


Step 2: In case you can't see the model, also make sure the used Shader (NormalMapping.fx (10.99 KB, for 3ds Max) for this model, make sure you have a NormalMapping.fx in your XNA Graphic engine) and all other required files exist in the same directory; like the normal map texture CactusNormal.dds (341.47 KB).
If you want to change an existing 3D model and let it use a shader, open up the Material Editor in 3DS Max (e.g. by using the M hotkey) and create a new DirectX shader material. To do that click on a free material spot, click Standard (1.) and then select DirectX Shader (2.), now select the shader you want to use and fill in all the required shader parameters (colors, textures, etc.).


Step 3: Now export the 3D Model as an .X file via the Panda DirectX Exporter mentioned above. Make sure settings in the Texture & .fx files and the X File Settings are as shown below. Most importantly you need to make sure all fx parameters are exported and the file does not use a left handed axis (default behavior) because our XNA engine defaults to the default right handed axis behavior in XNA. Using a binary file type will improve the file loading time.


Step 4: Now you should have the Cactus.X (26.21 KB) model file. You can drag this file and the textures into the Content directory of your project now (1.), then exclude the Cactus.dds and CactusNormal.dds textures (2.) because they are automatically generated by the dependencies in Cactus.X. Finally click properties of the Cactus.X file (3.) and make sure you use the XnaGraphicEngine Model (Tangent support) content processor (4.).


Step 5: Now with everything in place it is time to test if we can load the model and see it on the screen. I usually use the following unit test in the Model.cs class called TestSingleModel:


Step 6: Which finally leaves us with the result if everything worked out. It rarely does on the first try, go through the steps again and make sure all files are there and you can use the shader and textures in the engine as expected, you might have to rename shaders and directories and resave the .x file until everything is correct. BTW: I used the XnaRacingGame engine from http://XnaProjects.net here. Good luck :)
Comments [0] | | # 
 Monday, February 11, 2008
Monday, February 11, 2008 11:48:36 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Other | Programming | Racing Game | Reviews | Rocket Commander | XNA )
Even when I did not post much last week I was very busy converting all the old XNA 1.0 games to XNA 2.0. I did not only convert all projects (8 games in total, see below), but I also tested them extensively on Windows XP, Vista (32 and 64 bit) and the Xbox 360. Additionally a lot of usability improvements have been implemented in the games, for example the XNA Shooter is now much easier (was almost impossible to even reach 50% of the level) and a lot more fun due better balancing. The XNA Racing Game has now a better physic engine and will not longer let the car fly out of the track or leave ground in loopings. Due the better input control and fixed physics the cars drive now much faster and it is more challenging to complete the tracks in shorter time frames.

Games in this article:
Please read my previous post about Converting XNA 1.0 games to XNA 2.0 for all technical tips. All the games can also be found on http://XnaProjects.net, but I will also make them easier accessible on this blog soon, which has an update overdue (need to clean up the left and right sides) ^^

Thanks to the great VS2005 support of XNA 2.0 all games have now just one single solution file, which works on Windows and the Xbox 360. The projects can be opened in XNA Game Studio 2.0 and Visual Studio 2005 without having to convert the files over and over again like in the past. The Icons for all games were also improved. Lets take a look at the Icons (.ico files) on Windows:


For the Xbox 360 game icons the .png files (usually named GameThumbnail.png) are used:


Ok, let's take a look at the games and what has changed for them. Most games are pretty much the same as for XNA 1.0, but a lot of smaller bugs were fixed and they have been tested more.

  • Chapter1Game: This application is not really a game, but a test project to check out if XNA 2.0 is properly working on both Windows and the Xbox 360. It is from the first chapter of my book "Professional XNA Game Programming". BTW: The second edition of the book is coming out soon, there are 3 more chapters about Multiplayer game programming and a cool new role playing game.

  • Xna Pong: Xna Pong is a simple clone of the favorite pong game from 1978. It is just a few hunderd lines of code and should be very easy to understand.

    This game is from the book "Professional XNA Game Programming" by Benjamin Nitschke. For more information read chapter 2. (2008-02-10: Now updated to XNA 2.0)

  • Xna Breakout: XNA Breakout is a simple Breakout/Arcanoid game based on the XNA Pong game from the previous chapter.

    It is fully described and covered in Chapter 3 of my book "Professional XNA Game Programming". The code is quite short and should be easy to understand. (2008-02-10: Now updated to XNA 2.0)

  • Xna Tetris: This is a simple, but highly addictive Tetris game. You can control the blocks with your cursor keys, aswd or a game pad and the game works both on Windows and the Xbox 360. Reaching levels above 5 is really hard. My highest level was 9, try to reach more :) (2008-02-10: Now updated to XNA 2.0)

    This game introduces the helper classes (chapter 4 of my book) and makes more use of unit testing and game components in XNA.

  • Rocket Commander Xna: XNA port of the famous Rocket Commander game. The game principle stayed the same, but the controls were a little bit simplified to make it more fun on the Xbox 360.

    If you want to learn more about the Rocket Commander game, check out its official website www.RocketCommander.com and check out the Video Tutorials on Coding4Fun by MSDN. (2008-02-10: Now updated to XNA 2.0, also supports very big resolutions now and runs faster on the Xbox 360)

  • Xna Shooter: Shoot'n'up game specifically created for my book "Professional XNA Game Programming". It features full HDTV support, runs on Windows and the Xbox 360, 5 weapon types, 5 enemy types, a powerful ship and some power ups. It is quite fun to play and it gets harder and harder the longer you play. Based partly on the Rocket Commander XNA engine, but also features lots of new effects and shaders. (2008-02-10: Now updated to XNA 2.0, also much easier and balanced)

    This game and the racing game are the most improved. The game works now much better in high resolutions and on the Xbox 360. But most importantly the game is now much easier, balanced and more fun. Additionally a level percentage is now visible on the bottom and more EMP bombs can be picked up to make it easier at the end of the level.
  • Xna Racing Game: XNA Racing Game Starter Kit I wrote for http://creators.xna.com. More information and more downloads can be found on http://XnaRacingGame.com. It runs best on the Xbox 360 in HDTV (1920x1200), but it also runs fine on the PC. (2008-02-10: Now updated to XNA 2.0, driving also improved a lot, better tested on Xbox 360 and fixed some issues).

    Following things were improved: Shadow mapping on very big resolutions works now (crashed before), more options for lower quality settings, fixed physics, car now always stays on the road, fixed loopings, cars are much faster now, winning conditions work better now, and fixed several other bugs.
  • Dungeon Quest GDC: And finally the Dungeon Quest XNA Game, which was developed in just 4 days on the GDC 2007 at the XNA Contest. Dungeon Quest GDC is a relatively complex 3D role playing game (at least for just 4 days of work). An early version even supported coop multiplayer on the Xbox 360 via splitscreen. The game was developed by Benjamin Nitschke (abi.exDream.com) and Christoph Rienaecker (WAII). (2008-02-10: Now updated to XNA 2.0). This is NOT the full Dungeon Quest game (see www.DungeonQuestGame.com for that), this is just the GDC version.

    Please note that the level was reduced to allow loading on the Xbox 360 (which otherwise crashes with an OutOfMemoryException), the game is not fully playable, only the first part is implemented. You can also press F2 to toggle the Options menu and some minor bugs were fixed. But this game is no longer supported, I will not improve it anymore! Please check out the new Dungeon Quest game from www.DungeonQuestGame.com, which is coming in a month or so.
Have fun with all the games :)
Comments [9] | | # 
 Friday, February 01, 2008
Friday, February 01, 2008 12:32:32 AM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Other | Programming | Racing Game | Reviews | Rocket Commander | XNA )

When I wrote this (a little bit each day while working on converting the old XNA projects) I was very aware about the disappointment of my blog readers about the fact that I did not blog much in the last couple of months, especially on XNA. I not only got a lot of emails about that, but also quite a lot of questions, especially since XNA 2.0 was released. I made yet another promise to myself to change that and finally blog more, maybe not only when something very interesting pops up, but instead about the everyday issues I run into.

Some Notes about XNA 2.0: More solid, lots of little new features, networking, while it may not be a very complete solution, at least it is now possible on the Xbox 360 and overall I have the feeling even more people are interested in XNA than a year ago. Plus the guys at the XNA Team doing a great job and are constantly improving the XNA Creators Club website for us game programmers and artists :)


Several people had problems using the old XNA 1.0 code of my games and make them work with XNA 2.0, so here is a little help in case you want to convert XNA 1.0 projects to XNA 2.0. You will also notice this if you go to any XNA community site as most samples will still be in XNA 1.0 and not work out of the box in XNA 2.0, and many of those will probably never be changed since they are not longer actively being developed.

For most games almost all of the code can stay unchanged, you just have to poke at a few things that have changed in the framework or were improved. More information about converting projects can be found here (read this first, this article is based on the stuff there). You can also use the Cross-Platform Game Project Converter from XNA 2.0 to add a Xbox 360 project to your existing Windows XNA project without having to create a separate project (it is helpful, but I used pretty much the same trick for all of my XNA 1.0 games anyway).

Let's go through the steps:

  1. Either use the XNA project conversion utility (can be found on the XNA Creators Club website) or just create a new XNA 2.0 project in VS 2005.

  2. If you created a new project, drag all source code files into the project and seperate the content files out and put them all in the existing Content directory (only there the content pipeline is activated). If you just converted a project and the content files did not move, move them yourself to the content directory. Gladly all my projects with more than 5 content files had a special content directory anyway, so no need to change anything content-wise for them. If you don't want some of the files to be compiled to .xnb files, you have to change the build action from "compile" to "content" (and then use the "copy to output directory" switch) or to "none" if you want them to be ignored like for .wav files, which are automatically processed by the .xct (XACT) file for you.

  3. Find the line content = new ContentManager(Services); and replace it with Content.RootDirectory = "Content";. If you do that, get rid of the content manager in your game class since you can now use the build-in Content property to access the underlying Game content manager. In case you don't want to do that or if you need an extra variable, replace the above line with content = new ContentManager(Services, "Content");. Both ways will make sure all the content is now loaded from the content directory instead from the main directory of the application. In more complex XNA games you can also change the BaseGameDirectory to the content directory, but then you would also have to move all other resource files to this directory (config files, save games, levels, etc.). It is usually a good idea to separate the compiled (.xnb) content from the content the user can change (config, levels, etc.), so I suggest just redirecting the content directory of the content manager.

  4. Replace the LoadGraphicsContent(bool) method with LoadContent, remove all the if (loadAllContent) commands (was never false anyway, just let the content of the if loop stay) and also remove the call to base.LoadGraphicsContent(bool) (does not do anything like all the Load or Unload methods in the XNA Game class, they are just empty virtual methods). You can also ignore this and the next step since it will only generate depreciated warnings, but I suggest cleaning up your source code whenever an opportunity like this presents itself. I also added some missing region blocks to the code and some comments here and there were they were missing.

  5. Finally delete the UnloadGraphicsContent method unless it did anything beside base.Unload and base.UnloadGraphicsContent. In my XNA games the UnloadGraphicsContent usually looked like this and can be safely removed now (at least if nothing else is in there):
  6. /// <summary>
    /// Unload graphic content if the device gets lost.
    /// </summary>
    /// <param name="unloadAllContent">Unload everything?</param>
    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
      if (unloadAllContent == true)
        content.Unload();

      base.UnloadGraphicsContent(unloadAllContent);
    } // UnloadGraphicsContent(loadAllContent)


  7. In case you load sound and music via the AudioEngine, you have to change the directory to the content directory too, which will not be done automatically for you since you load the .xct file directly in the AudioEngine constructor. Basically just exchange the following code:

    audioEngine = new AudioEngine("YourSound.xgs");
    waveBank = new WaveBank(audioEngine, "Wave Bank.xwb");
    soundBank = new SoundBank(audioEngine, "Sound Bank.xsb");

    with:

    audioEngine = new AudioEngine("Content\\YourSound.xgs");
    waveBank = new WaveBank(audioEngine, "Content\\Wave Bank.xwb");
    soundBank = new SoundBank(audioEngine, "Content\\Sound Bank.xsb");

       
  8. In case you have used the StorageDevice and specifically the ShowStorageDeviceGuide helper method, it is gone now in XNA 2.0. I had it in some helper classes, but never actually used it. In case you want to show a save game dialog (or some network game select dialog for example), please follow the XNA 2.0 help instructions to do this asynchronously now.

  9. In case you use any ResourceUsage enum, replace it with TextureUsage instead or remove it if the issue is not texture related. You can also safely remove any ResourceManagementMode.Automatic parameters, which are not longer supported. Everything is now automatic anyway. Just if you have been using ResourceUsage.RenderTarget you will need to change the Texture2D class to a ResolveTexture2D class in order to archive the same behaviour as before. Some calls to the device (e.g. ResolveBackBuffer) have also changed and require a ResolveTexture2D now. You may also want to check if you have any manual texture management or disposing, which you can remove or simplify.

  10. For simpler games (2D) games you should be done now. More complex games using render targets and other features that have changed in XNA 2.0 will require some more changes, but after you have done them once (or know where to change what) this is also a quick process.

The following only applies to the RocketCommanderXna, XnaShooter and XnaRacingGame engines, but you might find similarities with other XNA games and the converting process:

  1. First of all make sure the old XNA 1.1 code gets compileable by going though the changes mentioned above (e.g. replacing ResourceUsage with TextureUsage or BufferUsage) and removing everything that does not exist anymore (like ResourceManagementMode.Automatic). If a method is non-existent in XNA 2.0 like ResolveRenderTarget, comment it out and remember where it happened.

  2. You might go through other issues, but you have to come back to the RenderTarget issue. This took the most time in the converting process for me (probably half of all my issues come by something related to changes with RenderTargets in XNA 2.0). For that reason always make sure that rendering to textures still works while you make changing. I always used the TestCreateRenderToTexture unit test inside the RenderToTexture class to figure things out.

  3. Additionally to making some changes in the BaseGame class (loading content via LoadContent, using the base.Content instead of creating a new content manager, etc.) I also removed all the RenderTarget helper methods and fields from the BaseGame class (SetRenderTarget, ResetRenderTarget, etc.) and moved them into the RenderToTexture class. While this makes the code more clean and restructured by making a few more fields private, if you do not call the new InitializeDepthBufferFormatAndMultisampling of the RenderToTexture class the calls to SetRenderTarget and ResetRenderTarget will not work correctly and will not restore the default depth buffer (which has to be remembered first). If you get the following exception it means the DepthBuffer Device.DepthStencilBuffer was set to null, but is obviously still used. In order to fix that make sure the remDepthBuffer variable is set to a correct value in the InitializeDepthBufferFormatAndMultisampling method!

  4. An error has occurred during the Clear operation while trying to clear the depth or stencil buffer, no DepthStencilBuffer surface exists.
    System.InvalidOperationException: An error has occurred during the Clear operation while trying to clear the depth or stencil buffer, no DepthStencilBuffer surface exists.
    at Microsoft.Xna.Framework.Graphics.GraphicsDevice.Clear(ClearOptions options, Color color, Single depth, Int32 stencil, Rectangle[] regions)
    at Microsoft.Xna.Framework.Graphics.GraphicsDevice.Clear(Color color)

       
  5. 4. Even if you have now done everything, the app may still crash when you are trying to clear a render target (which usually happens at the start of each pre or post screen shader). The reason for the following error is the multi sampling format, which might be set to the background buffer, but not to the render targets:


    The active render target and depth stencil surface must have the same pixel size and multisampling type.
    System.InvalidOperationException: The active render target and depth stencil surface must have the same pixel size and multisampling type.
    at Microsoft.Xna.Framework.Graphics.GraphicsDevice.VerifyDepthRenderTargetCompat()
    at Microsoft.Xna.Framework.Graphics.GraphicsDevice.Clear(ClearOptions options, Color color, Single depth, Int32 stencil, Rectangle[] regions)

       
    In order to get rid of this error without changing the RenderToTexture class a lot, you can just comment out the line where multi sampling is activated in BaseGame:

    //this.graphics.PreferMultiSampling = true;

There are probably even more things that I forgot while converting the projects (converted 8 games and about 15 projects in total now), but the above list should be helpful. Especially for me because I always forget some of those little things and having this checklist is very helpful.

Tomorrow I will probably test all the XNA 2.0 games on my Xbox 360 and make some final adjustments and then post them all on http://XnaProjects.net (and here).

Comments [0] | | # 
 Monday, January 28, 2008
Monday, January 28, 2008 3:21:17 PM (GMT Standard Time, UTC+00:00) ( Ajax | All | Development | IronPython | Programming | Reviews | Silverlight | XNA )
Hi everyone,

I prepared some new exciting blog posts, especially about XNA and I will post them shortly and also quite a few things will change at this blog. Stay tuned.

Recently one of my companies (namely realis) moved to Hamburg and I have a new PC at the new office, where I installed Visual Studio 2008 and everything else I needed. After I downloaded the latest source code files from Team System, I ran into some problems. BTW: The Team System trial will end next month and I will probably not switch back to SubVersion, but instead go back to Visual SourceSafe since the integration is the best in Visual Studio. Team System is nice to have but absolutely not something important for me as I'm most of the time the only guy using it anyways.

Back to the Problem. After I loaded the main solution the 2 web applications in it were unable to be loaded. The rest of the projects did load fine and worked after a while until I had all the missing Assemblies installed (xunit, Ajax stuff, Silverlight, IronPython, EntitySpaces, etc.). But even after making sure Silverlight and the Silverlight Tools for Visual Studio Alpha was installed and working (checked it by creating a new Silverlight project, which worked fine), I was still unable to load those projects. I thought at first maybe something went wrong when I checked in or out all the files, but after testing it on my laptop this was not the issue, all files are intact. Everything worked just fine on my laptop, where I had done most of my work during the move and first weeks until everything was finally working here.

After digging around a bit I went back to the IIS were those 2 websites should run on and they did not run yet (not compiled yet, no wonder). But even after I pointed to some dummy website the IIS did throw out some errors (first some dlls were missing, they were quickly replaced, then some security issues with Vista, also easy). Then I pointed back to the location of the projects and made sure IIS was working this time. Now I could go back to Visual Studio and finally load the web projects.

If you have such a InteropServices.COMException, try to see if you have missed something like the Silverlight Tools for Visual Studio Alpha and then figure out if your IIS is working. You can also edit the .csproj file and remove the references to IIS at the very end of the file to see if the project can then be loaded (using IIS again is not hard through the project properties).

Hope this helps (phase stolen from ScottGu, btw: really cool stuff going on over there, the ASP.NET MVC framework is nice and the .NET Framework SourceCode is very helpful!)

Comments [2] | | # 
 Thursday, December 13, 2007
Thursday, December 13, 2007 8:35:44 AM (GMT Standard Time, UTC+00:00) ( All | Development | Programming | Reviews | XNA )
As mentioned here in the official press message from Microsoft XNA 2.0 was released today after a very short beta phase which did only run for a few weeks.

You can download XNA 2.0 from Creators.Xna.Com, but for now it just says "XNA Game Studio 2.0 will be available early morning, Thursday Dec 13th, 2007 PST.". This will hopefully change soon ^^

http://creators.xna.com/Education/GettingStarted.aspx

Since XNA 2.0 is now out I will talk about some of its new features shortly and comment a little bit about the new networking API and Xbox LIVE features. Stay tuned.
Comments [6] | | # 
 Tuesday, November 20, 2007
Tuesday, November 20, 2007 12:24:41 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | Reviews | XNA )
The long awaited XNA Game Studio 2.0 beta is now available and can be downloaded from the XNA Creators Club website: http://creators.xna.com/beta/betahome.aspx

My first impression of the new multiplayer API is not that good. While it is very nice to have the ability to use networking on the Xbox 360, it is not only too hard to make it work on just a Windows PC and secondly you cannot expect that your gamers will both have a Xbox LIVE Gold membership AND a XNA Creators Club membership (both cost money). This means if you really want to do networking on a Windows XNA 2.0 game, you have to use System.Net and write it all yourself or no one will ever play your game on Windows except some XNA developers maybe.

Other than that XNA 2.0 is a great improvement, but it does only work with VS2005 yet, maybe they missed the VS2008 release earlier yesterday ^^ But it will probably be possible to modify the .csproj files again to make it work in VS2008 (without the extra XNA features and content pipeline).

Comments [0] | | # 
 Thursday, August 16, 2007
Thursday, August 16, 2007 8:59:02 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | Reviews | XNA )

Ziggyware has a good overview of the XNA 2.0 announcements and GameFest conference sessions in the following post. Check it out if you are interested in the new XNA 2.0 features (mainly networking, which is really a big step forward). It is also nice to see so many new XNA games and development efforts :)

http://www.ziggyware.com/news.php?readmore=407

Comments [0] | | # 
 Thursday, August 09, 2007
Thursday, August 09, 2007 6:43:04 PM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | Racing Game | Reviews | XNA )
Microsoft XNA: Ready for Prime Time?

is the name of the Article of the CoDe Magazine. It is very well written and a really long (9 pages) read with tons of information in it.
Together with 6 other guys I was interviewed about my experiences with XNA and the development process of the
XNA Racing Game and Dungeon Quest, which are 2 of the best looking XNA games so far :)

Check it out, good work Nick Landry.
Comments [2] | | # 
 Tuesday, July 31, 2007
Tuesday, July 31, 2007 6:35:30 AM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Programming | XNA )
Hey! Finally some News about Game Development again. All this Silverlight and VS2008 stuff is nice, but XNA has become pretty popular recently (lots of posts in blogs, more articles everywhere and a lot more news).

Anyway, I wanted to release the Dungeon Quest Source Code for several months now, but I never found the time to package everything up, do some cleanup and release it. Recently some people have requested the source code and emails keep coming in. To finally make everyone happy, here is the new Dungeon Quest version with some nice additions, much cleaner code with quite a lot of refactoring and some nice unit tests (see below). This version also does not require PS3.0 anymore, you can now run Dungeon Quest on a PS2.0 graphic card!

First of all: The downloads, all of them can be found on XnaProjects.net too:
  • DungeonQuestSetup.exe (40 MB): Game installer for Windows with everything you need to play.

  • DungeonQuestSourceCodeAndContent.zip (103 MB): Full Dungeon Quest source code with all content files.
    Please note that the collada files (xml text format) and the sound/music are very big (extracted ~200 MB).
    This download also contains the full game in the bin/debug/ directory, you only need to download this file
    for development and testing.
    Contains solution and project files for VS2005, VC# Express (XNA Express) and Xbox 360!

  • DungeonQuest.wmv (21 MB): Higher quality version of the YouTube video below

Here is the good old YouTube video from the game to give you a first view if you don't know about Dungeon Quest yet.



If you want to drive into the source code I highly recommend starting all the unit tests in Program.cs after checking out the directory structure of the project:

  • UIManager.TestUI();
    Show the graphical user interface of the game (boxes for health, player values, rpg levels, etc.)

  • ShaderTests.TestNormalMappingShader();
    Test the normal mapping shader, for more shader tests check out the XNA Rocket Commander and XNA Racing Game projects.

  • ColladaModel.TestCaveColladaModelScene();
    This tests the big cave collada file (80 MB) and renders it with the cave shader. Looks very nice.

  • ColladaModel.TestLoadStaticModel();
    This is straight from the SkiningColladaModelsWithXNA project from February. This test show shows a static collada model.

  • AnimatedColladaModel.TestPlayerColladaModelScene();
    Also from the SkinningColladaModelsWithXNA project. This shows the player or the goblins running, dying, staying, etc.
    This is a useful unit test to test animations for skinned collada models.

  • PostScreenGlow.TestPostScreenGlow();
    Test post screen glow, more details can be found in the Rocket Commander tutorials and most of my XNA projects.

  • PlaneRenderer.TestRenderingPlaneXY();
    Test to render a plane on the ground, which is also used in some other tests. Also good for testing shaders.

  • ShadowMapShader.TestShadowMapping();
    Test Shadow Mapping unit test, pretty complex stuff, but kinda nice for this game. More details can be found in my book, this is a complex topic and involves a lot of finetuning to get it right.

  • EffectManager.TestEffects();
    Since DungeonQuest uses some effects for fire, smoke, blood, etc. it has a simple effect system build in. Use this test to see whats possible and to extend the effect system.

  • StringHelper.TestConvertStringToFloatArray();
    An example of a dynamic unit test, which can also be started from NUnit or TestDriven.Net (right click if you have the addin installed).

  • Sound.TestPlaySounds();
    And another test to play the sound effects used in the game, just press A, B, 1, 2, 3, etc. to play sounds.

  • ColladaModel.TestCaveColladaModelSceneSplitScreen();
    This test shows how the engine supports split screen support, but it was never fully implemented into the game itself yet.
    To finish this the game logic + handling input from 2nd player has to be implemented.

Some Screenshots from the last post on my blog about Dungeon Quest:


Ok, let's get started. What is this guy doing there. He doesn't look very friendly, maybe it is better to hit him in the head!


First quest completed. This key is important to open the door to the second level.


Nonstop fighting action. Well, at least if you don't lose your way.


This is how it looks like after I got angry. I told those guys, don't start any fights!


Ohh no. I did not use all my skills (see right side) and this Ogre killed me too quickly. I guess I have to start over.


The credit screen when the game ends. Notice my book :)


No reason to stop, let's start over and try again. The fight is continuing. The game is not that hard, you just have to use all skills and avoid being hit by fireballs or big enemies when you have low hit points.

I hope you like Dungeon Quest and that the source code can be useful for your XNA projects. Enjoy!

Comments [11] | | # 
 Thursday, July 19, 2007
Thursday, July 19, 2007 3:21:43 AM (GMT Standard Time, UTC+00:00) ( All | Development | Game Development | Other |