On this page

CR_Commenter v1.8 - Autogenerate comments with this CodeRush plugin
Leaving Seattle USA and flying back to Germany - Friday (Day 7)
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)
MVP Summit 2008 in Seattle USA - Monday (Day 3)
Visiting USA Seattle 2008 - Day 2
Visiting USA Seattle 2008 - Day 1
Flying to Seattle - USA again, this time for the MVP Summit
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
This blog has moved!

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

 Monday, April 28, 2008
Monday, April 28, 2008 9:10:47 PM (GMT Standard Time, UTC+00:00) ( All | Development | Other | Programming | Reviews )
I worked on the CR_Commenter v1.8 Update all weekend and tested it a little bit today at work. 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 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

Here are the coolest new features in v1.8:
  • New and updated hotkeys, also made sure all of them work as advertised! You can also now switch on and off much more features and hotkeys if you like (e.g. disable Ctrl+1, Ctrl+3 or Ctrl+.). Normally the commenter will do his work every time you press '}' while coding and automatically add comments, xml blocks and the header (no need for any hotkeys during normal coding). But you can also press the following hotkeys to perform special actions:

    • Ctrl+1: Does perform the same action as closing a block with '}', but can be used anywhere inside a method or class.

    • Ctrl+2 or Ctrl+.: Adds a namespace for the current word at the cursor. Used to support automatically adding a namespace in C# 3.0 (back in v1.7), but now works the same way as VS 2008 plus saving you from pressing Enter to accept a namespace suggestion. If you like the VS2008 way better (allowing different namespaces), just disable Ctrl+. and VS will work as before.

    • Ctrl+3 or Ctrl+R: Auto generate region around the current block (method, property, constructor, etc.), for selections use Ctrl+R. You can also press Shift+Ctrl+3 to process the whole file! This feature was originally added in CodeRush v1.5, but abandoned in v1.6 and v1.7 after CodeRush supported it internally. Now the feature is back with lots of improvements, auto generating regions all over the place and producing region names for you. Can of course be disabled if you like the CodeRush way better (having to type the region name yourself).

    • Ctrl+4: Toggle collapsing and expanding current selection. This feature often caused trouble in v1.7 because it collapsed sometimes regions you were working in (e.g. method blocks with sub regions), now it will not longer disrupt your work and never close any region you are working on (except you want to with this hotkey).

    • Ctrl+5: Toggle collapsing and expanding everything at top level. Very cool to quickly check out what this file is about, use it in conjunction with first generating all region blocks (Shift+Ctrl+3).

    • Ctrl+6: Update all comments and xml blocks for the whole file. Was Ctrl+9 before and Ctrl+F9 even before that, this method is not required very often because Ctrl+1 handles most of this already. Please also note that Ctrl+7 was abondoned for now (resort language blocks and regions) because this feature is very complex and not implemented yet (see above for v2.0).

    • Ctrl+9 or Ctrl+F9: Generate all unit tests automatically for us (I use F9 to test, Ctrl+F9 makes sense to generate tests)! This will add a sub class called Tests, which contains all public methods as tests, which will all fail by default. Then all unit tests will have to be written and tested until everything is implemented correctly. Existing test methods will not be overwritten! This features is the major improvement in v1.8 for me, it will add unit tests automatically for us at the bottom of file to provide 100% code coverage (all unit tests should fail initially, add and change code to make them work).

  • Thought about auto sorting and removing namespaces for all files, but that works just fine with the new VS2008 Powertools.

  • 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);

  • Added option to not update comments on } (or Ctrl+1)

  • 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).

  • Press Shift+Ctrl+3 to generate all regions at once for the whole file! The commenter will also not longer produce regions were you already have some (even if they are different), especially if you press Ctrl+3 multiple times you will see what I mean ^^.

  • 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 :)
Some cool and highly complex ideas (like the auto refactoring and resorting of all code elements in a file) did not make it into v1.8 and will probably lay on ice for a while until I start developing CR_Commenter v2.0. But other than that I'm pretty happy with the new version and both the new region generation code and the new unit test generation code is really helpful, plus the many fixes and improvements of existing features make it more enjoyable to use CR_Commenter.

Download CR_Commenter v1.8 here (free as usual, you will need DXCore, which you can download here freely):
Comments [0] | | # 
 Sunday, April 20, 2008
Sunday, April 20, 2008 4:08:24 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews )
This is the final post of my picture set (7 Days of posting pictures, uff). Nothing much happened on Friday. I just woke up in the hotel, posted the Thursday post, watched some more GOM TV StarCraft Videos and finally left to the Seattle Airport. My camera batteries were depleted and most of the pictures were not recorded because of that. From time to time I was able to shake the batteries enough to make one or two pictures ^^



I hope this is not the plane that will take me all the way back to Europe :)

After basically just walking over to the gate the plane was not there yet. I expected one or two hours of security checks and waiting like in the past, but it took maybe five or ten minutes to walk through that. Strange .. maybe it was early or just luck.

Ah, there is my plane. My laptop batteries are almost dead by now and I will read the book I brought along (Digital Fortress). Sleeping in the plane is not an option, way too uncomfortable and too noisy.

Bad weather in Frankfurt - Germany. We slided quite a bit on the water on the runway. By now it is actually Saturday because of the 10 hour flight plus the 9 hours time difference.

Cool secret underground tunnel. The airport was so packed, it took over an hour to get to the security and finally reach my gate just in time. And all that stress just to find out that my plane to Hamburg has not arrived yet and will be 30 minutes late.

Finally reaching Hamburg Airport (looks much smaller, doesn't it?). It is about 1:00 PM here now and I'm awake for 18 hours now.

Not only is Hamburg Airport much smaller, there are no train connections. You have to take a bus first and then drive all around the city. Next time I will probably take the Airport Express Bus, which drives directly to Hamburg Hauptbahnhof (main train station). It is quicker to get back home from there for me.

I hope you enjoyed my pictures. Sorry for any misspellings I might have made, just contact me to fix links or names. Most of the time I wrote the text in a bus ride or while listening to a session. Next I will probably blog a little bit about ASP.NET MVC, Silverlight and/or IronPython.
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] | | # 
 Tuesday, April 15, 2008
Tuesday, April 15, 2008 6:31:08 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews )
Today (Monday, but it will be yesterday at the time I post this) was the first day of the MVP Summit 2008. BTW: I uploaded this post from the Microsoft Campus and it took around 2 hours to upload all the photos (2.5 MB ^^). Tomorrow I will do it from the Hotel again, no fun to do anything but a little surfing here.



After posting the blog post for Sunday (Day 2) from my hotel, the first thing I had to do was to move from my first Hotel (Renaissance) to the Seattle Conference Center and then later in the day to the Sheriton Hotel right beside it. The weather was similar to Sunday.

Several signs and banners were greeting the over 1700 MVPs and RDs that came to this event.

But before the opening welcome keynotes started it was time to eat some lunch first. The room was quite big and the food was good as usual :)

The keynotes were a little bit boring at first, but when Sean O'Driscoll came out and talked about MVPs and the evolution of Social Networking, it was very enjoyable because he is such a good speaker. You can see him on the picture above talking about Web 2.0.

After the welcome speaches the Open Space sessions started, which basically means you get together with a couple of other MVPs and everyone discusses around a given topic.

I attended the MVC and Unit Testing sessions and both were interessting, but the Testing discussion was more fruitful IMO.

This is one of the slides that were developed in the beginning of the Unit Testing session. I guess the discussion will continue somewhere on a blog or wiki somewhat soon.

The wheater outside was rainy again. I had to wait a bit until my hotel room was ready, so I walked around a bit trying to find any shopping stores that interested me. Let's just say I was not very interested at all the cloathing stores and restaurants here.

There is always construction of new sky scrappers going on here.

Still cold and raining ...

I ended up in "Little Italy" for a while, but again notthing of interest here for me.

This reminded me of a picture I took at my first trip to the US (Los Angeles in 2005).

Outside the Seattle Art Museum there was a big metal guy standing and waving his arm (or hamering something, I'm not that good at Art ^^).

Okay, and other wishes can also be satisfied here. Strange location for such a place, right next to the Seattle Art Museum.

The highway to the west and the Seattle Habour behind it.

Again the Public Libary of Seattle. This time it is open, but I'm back on my way to the hotel, my room should be ready now.

After finally checking in I had to go back to the Convention Center because the Welcome Dinner began right now and I had the chance once again to meet with the other MVPs from Germany, Switzerland and Austria.

Some other guys from Germany at the Welcome Dinner. As you can clearly see they are "Men" as it states right above their heads :)

The camera war has begun!

This is Peter Bucher in the middle, we talked quite a bit this evening. He is an ASP.NET MVP.

Some more German MVPs.

This guy had constantly problems with his camera, which was obviously better than all our other cameras combined :) He also took aprox. 10 times more pictures than everyone else at the MVP Summit combined ^^

And thats it for today. All other pictures were as blurry as my vision this evening after drinking all kinds of beer they had to offer here (I think it was Heiniken, Red Hook, MSC something, and something else, can't remember).

Tomorrow all the sessions will take place at the Microsoft Campus. Until then ..
Comments [1] | | # 
 Monday, April 14, 2008
Monday, April 14, 2008 4:33:55 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews )
Well, most of the pictures today are still from Saturday because I made so many that day. At Sunday the weather here was like in Germany, rainy and cold. I went a little shopping and went to a movie theatre, which was very enjoyable. In the evening I went to a MVP Germany meeting, but my Camera batteries died. But many other people made photos, I will post a link as soon as I know where all the photos are going to be uploaded.



The Space Needle I visited yesterday.

Well, it is not cheap to get up there. And due the good weather a lot of people had the same thought I had.

After waiting about 30 minutes in line I finally was driving up.

The buildings on the ground are getting smaller and smaller.

Great view at the top. You can walk on the inside and the outside and view in any direction you like.

The habour south of the Space Needle.

A big ship in the south west. It probably is too big to move any closer.

The waterfountain I posted yesterday from above. Its surreal how small the people are. It was a lot of fun zooming in at people and stuff going on down there.

A lot of buildings. Reminded me a bit of Ghost Recon. The jpg compression is also not working well on this image, it is more twice the size than the other images here ^^

Lake Union in the south east. My hotel was located there last time. The good weather also has its effects on the people down there :)

And to the east there is Lake Washington (hardly visible on this image) and behind that are Kirkland, Redmond, Bellevue and so on.

And finally Seattle Downtown south of the Space Needle. The MVP Summit will be on the very lower left side ^^

Mount Rainer, the highest mountain here, was also visible this day.

The water shader effects are certainly nice to look at. After spending about an hour on top of the Space Needle I decided to go back down to where all the action is.

And this is how Seattle looks at night at the daytime if you look at some monitors displaying night time in the middle of the day. Hopefully this sounded confusing enough.

A look up the Space Needle from below again.

A big red thingy.

In the Pacific Science Center you can watch movies in 3D, but all the movies were pretty boring (only kids stuff).

More stuff for kids in the Seattle Center Center Hall (what a stupid name). I ate some icecream and went back outside for the rest of the day exploring Seattle.

Even at bright daylight, if you take a picture directly of the sun, the surrounding area looks kinda dark. Confusing again ..

At sunday the weather was bad as I said above. I did not take many pictures. Here you can see my in the elevator playing around with my camera.

At the movies .. I saw Street Kings. It was an good movie with a lot of action and twists.

The public libary looks pretty cool. It is also quite big, but it was closed at Sunday.

And finally the Bank of America Towers, the biggest building here in Seattle. The weather was getting worse and it began to rain. I went back to my hotel and went to the MVP Germany meeting a few hours later (but no pictures because of my empty camera batteries).

And today (Monday) the MVP Summit will start. I'm pretty excited about this.
Comments [0] | | # 
Monday, April 14, 2008 1:45:24 AM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews )
Since I had no internet access yesterday, this is the post from yesterday. It was great wheater here in Seattle when our plane arrived here and much hotter than in good old Germany. I took so many pictures that I will only post a few of them today and more tomorrow, but this post is still big, there are 26 pictures in here. I'm also trying to display them differently this time in a table, not sure if this is a good idea or not, but it should make the post shorter and give you a better overview about all days in case I keep up posting this much images (hehe, I love my new camera).



Starting from Hamburg - Germany at around 7 o'clock. Weather is cold as usual (have not experienced anything else because I just live there for 3 months now ^^).

After playing around with my Camera and getting more annoyed with my Zune after I installed the latest firmware yesterday (Zune software just sucks, same as any stupid iTunes stuff) at Hamburg airport and Frankfurt airport my plane to Seattle left at around 11:00 am from Frankfurt.

Germany is getting smaller and smaller and I tried to take a few shorts with maximum zoom but they all were to blurry, so I have chosen this picture instead. From now on the whole flight was above clouds and it seems like it was bad weather all over the place.

Over Greenland I some ground again, but it does look all icy and unpleasant down there ^^ The plane ride was actually quite nice, each seat had its own entertainment system, which crashed only twice during the flight (it was running Windows CE). Also my first time flying with Lufthansa to the US. As usual I could not sleep during the flight so I read a book and watched some movies and TV shows.

At around 11:30 AM we have been flying for over 9 hours but the local time tells us different. After flying over the Cascades (see picture) and later Mount Rainer we landed in Seattle. The immigration guy told me that it seems like half our plane was full of people attending the MVP Summit :)

And here we see Kirkland and Redmond where Microsoft is located. On the very left you can also see Seattle Downtown.

This is a better picture of Seattle. I zoomed in a bit because we were still kinda far away. The plane was originally approaching from the North, but after making a big curve we landed from the South. The airport seemed to be quite busy and that must be the reason for taking a bigger detour.

At the ground and after getting through all the extra security checks (well, here is your luggage, but hey, just give it back to us in a few minutes, we will check it again) I decided to take a bus to Seattle Downtown, which turned out to be the stupidest idea of the day.

Not only did the bus arrive 40 minutes behind schedule, it was also packed and the passengers were complaining how stupid the driver is. It seemed like he drove the wrong way and made stops where this bus is not supposed to stop. Next time I will take a cab again, I did not even find my hotel at first because no one seems to bother putting up street numbers anywhere on the houses. An impossibility in Germany.

But even without street numbers these buildings always amaze me. Frankfurt has some skyscrapers too, but in Hannover or Hamburg buildings do not seem to get very high and they certainly are not sitting right next to each other ^^

I finally reached my hotel and I got a nice view on the east side of Seattle, but the weather is just too nice to go to sleep now (just up for 19 hours, nothing special for a game programmer ^^).

Instead I decided to go down to steet level again and explore Seattle a little bit more. Last time I visited I skipped some attractions.

My hotel for the first two nights is this one, the Seattle Renaissance Hotel with 28 floors. I'm pretty much in the middle at floor 15. From Monday on I will stay at another Hotel sponsored by the MVP Summit :)

The sun is still high and the weather was really nice at around 18 degrees celisus, much better than in Germany and better than the last time I visited. It was nice to be able to run around with just a shirt on (ok, I had pants too).

Some flags outside a building. Can you spot the one that seems to appear twice?

On the left side you an see a church. Hmm, churches to certainly look much different in Germany.

Shadow mapping, reflection and lighting effects are working out quite nicely in this real-world-engine.

In the back you can see the Washington State Convention and Trade Center in Seattle where the MVP Summit will be held (for the most part I think).

Can you see the girl running through this waterfall?

This is ridiculous. If you want to wiretap someone, do not have a 10 meter high antenna sticking out of your car roof. No way no one did not notice this thingy.

Okay, where is the Monorail so I do not have to walk all the way over to the Seattle Center park. Ohh, I guess it is over there.

Last time I was here the ride was free, now it costs 2 dollars. Well, still better than walking and since a dollar is only 0.62 Euro cents, it is not that much ^^

For some reason I think these people are doing something wrong. It might be fun to walk down a roller coaster, but I guess there must be some other way to make it even more fun.

I took many pictures of the Space Needle and many pictures from the top, but I already posted enough pictures for today. I will post these pictures tomorrow. This is a big fountain in the middle of the Seattle Center park.

The sun decided to go to sleep, maybe I should think about it too after being up for almost 24 hours now (well, the day is not over yet).

If I had such a limousine getting back to the hotel would have been easier I guess. But instead of going back directly I went to shop a little bit. Gotta love the fact that most stores have opened up almost all the time and any day of the week.


More pictures tomorrow then (or actually later today I guess).
Comments [6] | | # 
 Friday, April 11, 2008
Friday, April 11, 2008 9:09:54 PM (GMT Standard Time, UTC+00:00) ( All | Conferences | Other | Reviews )
Tomorrow morning I will fly to Seattle - USA again and make many pictures with my new camera (no cell-phone pics anymore ^^). The MVP Summit starts Monday, but it was cheaper to fly Saturday and I will have 2 days visiting Seattle again. Hopefully I will have Internet Access every day so I can upload pictures I took over there.

I have to go to sleep now, my journey starts in a few hours and sleeping in the plane never worked out great for me :)

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] | | # 
 Tuesday, April 08, 2008
Tuesday, April 08, 2008 9:13:21 PM (GMT Standard Time, UTC+00:00) ( All | Other )
Hi there.

This blog was previously hosted at http://exdream.no-ip.info and usually accessed via http://abi (as stated in the old header image). These links and all other still work, but now redirect to this new blog site: http://BenjaminNitschke.com

Hopefully this changes will not damage any old link, but just in case I'm letting the old server stay on and handle any old requests to http://exdream.no-ip.info links.

Hopefully everything will be better now :)

Comments [0] | | # 

Search

Games, Tools & Code

XNA Racing Game

CR_Commenter

NormalMapCompressor

Search XnaProjects.net for all my XNA projects

XNA Shooter

RocketCommander + Mods

AbiKeyboardLayout

Current projects

Dungeon Quest RPG (coming soon)

My book: Professional XNA Game Programming (May 2007)

MeinSport.de - German Sport Community Site