I went to the Siggraph event "The Art of Lighting and Rendering Rio" for the new animated movie Rio http://siggraph.ca/festivals/information.php?fest=20110621-RO which featured Jim Gettinger, Lighting Supervisor at Blue Sky Studios. Blue Sky Studios (New York) is owned by Twentieth Century Fox and has a pretty good 3D rendering history including Ice Age movies, Robots, Horton Hears a Who and more.
Gettinger talked about a number of aspects that were important to lighting Rio as well as initial concepts for creativity and direction. The first focus in the 3D movie is chosing the style. For Rio they went with ~80 percent realism (higher for human characters), 20% stylized, which can be seen in the stylized yet recognizable shapes in the movie. Probably the most interesting bit of news was the proprietary tool called "Studio" that Blue Sky Studio uses. I suspect it is a layer that sits on top of Maya and has its own renderer or sits on top of Mental Ray that allows for more level details to be exposed through tools for the artists. Some of the functionality includes:
- Voxels for feathers, plants and eventually buildings.
- Procedural textures on all background terrain and buildings (including all of the panoramic screen shots in the movie).
- Atmospheric pass that interacts with the raytracer.
There is not a lot of information about the CGI Studio propreitary software, but there is a link describing a little more at http://www.blueskystudios.com/content/process-tools.php but that was of course the most interesting part of the talk for me.
Some of the other information that was interesting was they had up to a 13 hour render for some of the frames on shots on Rio. There was a lot of compositing work and complexity of the shots (reflections, shadows etc.) dealing with the human characters' glasses. Some of the work invovled creating a separate pass for blending with refraction index of 1.1 (instead of 1.5 for glass), to give a more realistic look. There was also quite a lot of research and development done on the skin of the human characters, especially related to subsurface scattering. They needed to update subsurface scattering for different areas of face, creating additional maps for those areas that need scattering like on the character's nose.
An interesting talk, in an interesting building (looks like an art gallery on the outside, a bar on the inside and a theater inside that :P).
Looks like I will have to check out that movie now,
Michael Hubbard
http://michaelhubbard.ca
Wednesday, June 22, 2011
Tuesday, May 31, 2011
"Make Your Dreams a Reality" 3vis Event in Vancouver
I attended the 3vis event "Make Your Dreams a Reality" in Vancouver http://www.3vis.com/enCA/evenements which showcased a lot of Autodesk 2012 software including 3ds Max, Maya, Motion Builder, Mudbox and XSI. 3vis put on a nice show with some prizes, food and open bar :) The event was broken up into three major presentations.
The first presentation was by Louis Marcoux, who talked about 3ds Max and Motion Builder. 3ds Max has a new "Nitrous" viewport in 2012 that has multithreaded support, SSAO (Screen Space Ambient Occlusion), Soft Shadows and Indirect Illumination, which held up pretty well in a fairly detailed model of a town. One of the cool new feature is the stylized rendering which allows for different fx to be applied to the render, which give the look of Photoshop filters. The substance procedural textures was also pretty interesting, and looked like you could create a wide variety of pattern textures with minimal effort. There was also a small demo on the iRay renderer, which looked pretty nice, although the fact that you choose when it is complete makes me wonder how much extra processing time will occur with no visibile improvements as people do not know the correct range to get the results they want... I would have to play with it a bit to see if this was an issue I guess. Louis also talked about how easily the Autodesk packages integrate with each other, and gave an example of moving between Max and Motion Builder, where he had setup some motion capture data with his Xbox Kinect... I will have to look closer into some of this Kinect stuff, with my Kinect soon.
The next talk was by Lee Fraser on Maya 2012 and Mudbox 2012. The talk focused on some of the new improvements with Maya Viewport 2.0 for more realtime rendering options (which is great for closely matching game rendering). Maya 2012 has improved audio options, sequencer and "uber cam" options for in Maya editing of shot sequences. Some of the fluid option for liquids in Maya give some nice realistic results. There are also some improvements to the modeling system that allow curves to be used to shape and cutout polygons from a polygon mesh. The Turtle render is used for advanced baking options for lightmaps, and had some nice results. The Mudbox options included support for Linux, general improvements for mapping textures onto geometry, and improvements for organizing UVs.
The final talk was by Mark Schoennagel and his talk was on XSI, specifically on ICE. From a developers perspective this was probably the neatest talk (from an artist probably not the most intersting), as Mark went through the process of creating different ice nodes for both custom and recreating built in XSI features (which showed how almost any effect could be reverse engineered using ICE). Some of the most interesting things were using ICE to create an internal ray tracer within XSI, effects that change particles from one form into another, and the many ways ICE can be used to create cool fx.
This was a lot of fun, and I learned some cool things.
Bye for now,
Michael Hubbard
http://michaelhubbard.ca
The first presentation was by Louis Marcoux, who talked about 3ds Max and Motion Builder. 3ds Max has a new "Nitrous" viewport in 2012 that has multithreaded support, SSAO (Screen Space Ambient Occlusion), Soft Shadows and Indirect Illumination, which held up pretty well in a fairly detailed model of a town. One of the cool new feature is the stylized rendering which allows for different fx to be applied to the render, which give the look of Photoshop filters. The substance procedural textures was also pretty interesting, and looked like you could create a wide variety of pattern textures with minimal effort. There was also a small demo on the iRay renderer, which looked pretty nice, although the fact that you choose when it is complete makes me wonder how much extra processing time will occur with no visibile improvements as people do not know the correct range to get the results they want... I would have to play with it a bit to see if this was an issue I guess. Louis also talked about how easily the Autodesk packages integrate with each other, and gave an example of moving between Max and Motion Builder, where he had setup some motion capture data with his Xbox Kinect... I will have to look closer into some of this Kinect stuff, with my Kinect soon.
The next talk was by Lee Fraser on Maya 2012 and Mudbox 2012. The talk focused on some of the new improvements with Maya Viewport 2.0 for more realtime rendering options (which is great for closely matching game rendering). Maya 2012 has improved audio options, sequencer and "uber cam" options for in Maya editing of shot sequences. Some of the fluid option for liquids in Maya give some nice realistic results. There are also some improvements to the modeling system that allow curves to be used to shape and cutout polygons from a polygon mesh. The Turtle render is used for advanced baking options for lightmaps, and had some nice results. The Mudbox options included support for Linux, general improvements for mapping textures onto geometry, and improvements for organizing UVs.
The final talk was by Mark Schoennagel and his talk was on XSI, specifically on ICE. From a developers perspective this was probably the neatest talk (from an artist probably not the most intersting), as Mark went through the process of creating different ice nodes for both custom and recreating built in XSI features (which showed how almost any effect could be reverse engineered using ICE). Some of the most interesting things were using ICE to create an internal ray tracer within XSI, effects that change particles from one form into another, and the many ways ICE can be used to create cool fx.
This was a lot of fun, and I learned some cool things.
Bye for now,
Michael Hubbard
http://michaelhubbard.ca
Sunday, May 22, 2011
Full Indie Meetup
I went to another Full Indie meetup http://www.fullindie.com/2011/05/12/full-indie-anniversary-event-thursday-may-19th/ this one had a talk with Chris Stewart from Barking Dog studios and Andy Moore from Radial Games. The talk was good, quite a lot of people coming out, and a good motivator for going home and working on your own games.
I especially like some of the stuff that Chris had to say, it was along the lines of "fake it till you make it", but in more graphic terms. From his experience, there can be a lot of stress in being a indie game dev, and a lot of the business aspects of the job can prove to be more challenging than expected, even for a developer that is used to a lot of hard challenges. He mentioned that nothing will "kill you", and you just have to go out there and do it. The talk also contained some aspects of how to deal with people. People are the most difficult part of any company, and it is important to do your best to move everyone in the same direction. It is useful to group artists and developers together since they are the least likely to talk, and it is worthwhile in a small group that everyone has a lot of communication with each other, since there is often not someone around to manage all the important aspects.
Overall, I think that the talks were good, I did not stay too long as I had other plans, but look forward to more of these at some point.
Cheers,
Michael Hubbard
http://michaelhubbard.ca
I especially like some of the stuff that Chris had to say, it was along the lines of "fake it till you make it", but in more graphic terms. From his experience, there can be a lot of stress in being a indie game dev, and a lot of the business aspects of the job can prove to be more challenging than expected, even for a developer that is used to a lot of hard challenges. He mentioned that nothing will "kill you", and you just have to go out there and do it. The talk also contained some aspects of how to deal with people. People are the most difficult part of any company, and it is important to do your best to move everyone in the same direction. It is useful to group artists and developers together since they are the least likely to talk, and it is worthwhile in a small group that everyone has a lot of communication with each other, since there is often not someone around to manage all the important aspects.
Overall, I think that the talks were good, I did not stay too long as I had other plans, but look forward to more of these at some point.
Cheers,
Michael Hubbard
http://michaelhubbard.ca
Sunday, May 8, 2011
Book Review: Game Programming Golden Rules
The Game Programming Golden Rules by Martin Brownlow offers nine chapters on various game programming topics. For this review I will go through the chapters with a brief comment on each of them.
1. Embracing C++: This chapter is all about using the compiler to your advantage, the preprocessor, macros and other C++ goodies that are not so much game specific and it is making sure you know the language you are working in, and how best to save you time.
2. Orders of Complexity: Some Big O notations, Self Balancing Binary Trees, Red Black Trees, and Binary Space Partitioning (BSP) Trees.
3. Hashtables: Including creating hashtables and hash functions, reducing hash collision frequency, file name hashes, and probably the most interesting part of this chapter DNA hash for vertex shaders. Localization of text assets was also in this chapter, although I think that for a large game, an approach that used key value pairs in the same file is more maintainable than having the keys and values in separate file, since if a file gets very long it will be difficult for the person writing it (often more proficient in language than coding) to debug why the lines are not matching up properly.
4. Scripting: Creating your own scripting language with some sub rules: scripts should be easy to create, scripts should never crash the game, scripts should be dynamically loaded, scripts should handle multitasking, scripts should have a debugger, scripts should be extendable, scripts should never break the game build.
5. The Resource Pipeline: Handles some of the resource pipeline tools, similar to the Game Asset Pipeline book, suggests an intermediate format for all assets that the artists create, that has everything possible that you can think to include in the intermediate format (version numbers, dates, authors, etc.). The book also focuses on some file compression, platform differences, and cummulates in a version of a build assistant (called DataCon by Brownlow), which is a simple example of what should be the case for all development teams, a one-click build system.
6. Processing Assets: Includes, fonts, images, geometry and creating triangle strips.
7. Finite State Machines: Explicit vs. implicit state machines, scripting FSMs, creating combos, linking FSM objects, indirect animation lookups to allow the same FSM to be used for each character type (with different animations).
8. Saving Game State: The difficulties of saving game state, saving and loading a game and autodetecting object changes with a saving template.
9. Optimization: Measure Twice, Cut Once, using profilers, realizing that some optimizations do not actually improve the overall process, since all the moving parts can actually make it slower.
Overall the book was interesting, although a lot of the techniques I think could apply to any field and are not just game specific. I think this book would be good for beginners to intermediate and the source code that is provided is pretty clear for the examples.
Happy coding,
Michael Hubbard
http://michaelhubbard.ca
1. Embracing C++: This chapter is all about using the compiler to your advantage, the preprocessor, macros and other C++ goodies that are not so much game specific and it is making sure you know the language you are working in, and how best to save you time.
2. Orders of Complexity: Some Big O notations, Self Balancing Binary Trees, Red Black Trees, and Binary Space Partitioning (BSP) Trees.
3. Hashtables: Including creating hashtables and hash functions, reducing hash collision frequency, file name hashes, and probably the most interesting part of this chapter DNA hash for vertex shaders. Localization of text assets was also in this chapter, although I think that for a large game, an approach that used key value pairs in the same file is more maintainable than having the keys and values in separate file, since if a file gets very long it will be difficult for the person writing it (often more proficient in language than coding) to debug why the lines are not matching up properly.
4. Scripting: Creating your own scripting language with some sub rules: scripts should be easy to create, scripts should never crash the game, scripts should be dynamically loaded, scripts should handle multitasking, scripts should have a debugger, scripts should be extendable, scripts should never break the game build.
5. The Resource Pipeline: Handles some of the resource pipeline tools, similar to the Game Asset Pipeline book, suggests an intermediate format for all assets that the artists create, that has everything possible that you can think to include in the intermediate format (version numbers, dates, authors, etc.). The book also focuses on some file compression, platform differences, and cummulates in a version of a build assistant (called DataCon by Brownlow), which is a simple example of what should be the case for all development teams, a one-click build system.
6. Processing Assets: Includes, fonts, images, geometry and creating triangle strips.
7. Finite State Machines: Explicit vs. implicit state machines, scripting FSMs, creating combos, linking FSM objects, indirect animation lookups to allow the same FSM to be used for each character type (with different animations).
8. Saving Game State: The difficulties of saving game state, saving and loading a game and autodetecting object changes with a saving template.
9. Optimization: Measure Twice, Cut Once, using profilers, realizing that some optimizations do not actually improve the overall process, since all the moving parts can actually make it slower.
Overall the book was interesting, although a lot of the techniques I think could apply to any field and are not just game specific. I think this book would be good for beginners to intermediate and the source code that is provided is pretty clear for the examples.
Happy coding,
Michael Hubbard
http://michaelhubbard.ca
Sunday, May 1, 2011
Book Review: The Game Asset Pipeline
The Game Asset Pipeline by Ben Carter is interesting in that it is probably one of the only books out there on asset pipelines for games. The books is broken into too major parts: high level asset management, and low level processing details. Overall, the book handles these issues effectively and with some interesting insight into pipeline work in general.
Carter suggests a number of useful tips for those working on a asset pipeline. The output of all systems should be a custom intermediate file format. This will allow the intermediate files to be processed and transformed into final assets without the need to re-export all of the assets from the 3D applications (or other tools). By having them as simple text files, it will also allow any additional last minute processes, without the huge overhead of having to re-export from multiple tools (especially since the end format is likely to change). Since ideally the format should be as close as possible to what will actually be contained in memory while the game is running, the intermediate format can allow for processing into the final assets, without having to constantly re-export the same assets everytime the end file format changes (and it likely will a lot, especially at the end). Also the tools should do as much preprocessing as possible to ensure speed at game runtime.
The eternal debate of text vs binary files is also brought up in the book. It is recommended that text files are used (although binary can be brought in later if necessary), but more so than just using text files, is the idea of text files with an implicit structure. If a text file has no structure such as:
It becomes very difficult to add things in and the parser will likely be confusing since it is expecting things in a certain order. A much better approach is to have the structure built into the file such as:
With that kind of approach it is much easier to see what the structure is, and with a parser of this type, things can be added, reordered or removed without issue (such as if the AIType does not have to be set or whatever). A still better approach to the plain text file is some sort of structured file format like XML or JSON. While I like JSON for most things, as it closely resembles data structures, it is important to note that once you start getting into very large files, it becomes trickier to navigate all the closing brackets in JSON, whereas XML gives all the end tags which is somewhat easier to navigate. Both are entirely capable of getting the job done, but it is best to look at what languages you are using and what kind of support they have for parsing (C# and XML go hand in hand, as do more web languages like Javascript with JSON).
Carter continues with some of the high level asset management and that it is different than source control (although there are some basic similarities, the size of the assets, the number of binary files, etc.) makes for different challenges in an asset management system. The most important aspect of an asset management system are turnaround time, metadata about the assets, versioning and handling broken data. The book looks at a number of subversion systems including SVN, Perforce, CVS and Alienbrain. The use of databases (like MySQL or Oracle) are not usually the best options unless the database is setup to allow for huge file sizes.
The concepts of a client/server architecture for the asset management system is essential, and should support locking and unlocking files, versioning of files, preview functionality, showing modifications and some sort of caching system to improve performance.
The rest of the book is on more low level details such as texture, geometry, environment processing, asset dependency analysis and final data output. It is interesting, but is more platform specific than the high level concepts on asset management in the earlier chapters. In some ways, I would have preferred more of an expansion on the asset pipeline aspects and less on the low-level details (perhaps splitting up the two major topics into two separate books and get everyone to buy both :P)
Overall a good book on many of the concepts of the asset pipeline for those not familar, and is a good resource on a topic with not a lot of literature on it.
Bye for now,
Michael Hubbard
http://michaelhubbard.ca
Carter suggests a number of useful tips for those working on a asset pipeline. The output of all systems should be a custom intermediate file format. This will allow the intermediate files to be processed and transformed into final assets without the need to re-export all of the assets from the 3D applications (or other tools). By having them as simple text files, it will also allow any additional last minute processes, without the huge overhead of having to re-export from multiple tools (especially since the end format is likely to change). Since ideally the format should be as close as possible to what will actually be contained in memory while the game is running, the intermediate format can allow for processing into the final assets, without having to constantly re-export the same assets everytime the end file format changes (and it likely will a lot, especially at the end). Also the tools should do as much preprocessing as possible to ensure speed at game runtime.
The eternal debate of text vs binary files is also brought up in the book. It is recommended that text files are used (although binary can be brought in later if necessary), but more so than just using text files, is the idea of text files with an implicit structure. If a text file has no structure such as:
Fred
Solider
Aggressive
1000It becomes very difficult to add things in and the parser will likely be confusing since it is expecting things in a certain order. A much better approach is to have the structure built into the file such as:
CharacterName = Fred
CharacterType = Soldier
AIType = Aggresive
HitPoints = 1000With that kind of approach it is much easier to see what the structure is, and with a parser of this type, things can be added, reordered or removed without issue (such as if the AIType does not have to be set or whatever). A still better approach to the plain text file is some sort of structured file format like XML or JSON. While I like JSON for most things, as it closely resembles data structures, it is important to note that once you start getting into very large files, it becomes trickier to navigate all the closing brackets in JSON, whereas XML gives all the end tags which is somewhat easier to navigate. Both are entirely capable of getting the job done, but it is best to look at what languages you are using and what kind of support they have for parsing (C# and XML go hand in hand, as do more web languages like Javascript with JSON).
Carter continues with some of the high level asset management and that it is different than source control (although there are some basic similarities, the size of the assets, the number of binary files, etc.) makes for different challenges in an asset management system. The most important aspect of an asset management system are turnaround time, metadata about the assets, versioning and handling broken data. The book looks at a number of subversion systems including SVN, Perforce, CVS and Alienbrain. The use of databases (like MySQL or Oracle) are not usually the best options unless the database is setup to allow for huge file sizes.
The concepts of a client/server architecture for the asset management system is essential, and should support locking and unlocking files, versioning of files, preview functionality, showing modifications and some sort of caching system to improve performance.
The rest of the book is on more low level details such as texture, geometry, environment processing, asset dependency analysis and final data output. It is interesting, but is more platform specific than the high level concepts on asset management in the earlier chapters. In some ways, I would have preferred more of an expansion on the asset pipeline aspects and less on the low-level details (perhaps splitting up the two major topics into two separate books and get everyone to buy both :P)
Overall a good book on many of the concepts of the asset pipeline for those not familar, and is a good resource on a topic with not a lot of literature on it.
Bye for now,
Michael Hubbard
http://michaelhubbard.ca
Friday, April 22, 2011
Book Review: Game Engine Toolset Development
Not so much a programming book as much as it is a look into the entire world of tools development, Game Engine Toolset Development by Graham Wihlidal is an interesting read for those wanting to improve the production pipeline or tool creation. Now that I my current job is focused more on the technical artist/programmer instead of a team lead game programmer, I figure I should take a bit more time on the technical artist and tools building aspects (which are for me back to basics). The book focuses on a wide range of topics, and focuses more so on implementation details than tool design.
The book begin largely on the politics of tools and maintenance and different stakeholders in the tools, as well as some examples of different environments in which tools are created and maintained. There are a few examples of professional studios, who focus on elaborate GUI systems, and WYSISYG (what you see is what you get) world editors.
From there, the book has a chapter on why to create tools with C# with .Net (which is the language and framework used throughout the book). While I agree that .Net can be a great and quick framework for developing solid tools quickly, I would recommend sticking with the language that is closest to the game engine or 3D application whenever possible (since it is often the case that some parts (or the entire tool) can be merged into the game code, and it is better to not have too many languages in any production environment (if possible)).
There is a lot of focus on coding standards, design and documentation which is extremely important for tools, since they are often written specifically for one project, and can be very difficult to port to other projects if the design and standards are poor. Plus, the fact that many tools are written once and then the programmer moves on to something else, it is easier to forget why the code was written a certain way when come back to it in six months. Having a good design and (up-to-date) documentation is very important if no one is going to be looking at the code until the next project, or something breaks. There is some discussion of code metrics, UI design, using NUnit for unit testing, Microsoft coding conventions and using FxCop to enforce coding policies (I also like StyleCop for coding standards). The coding documentation is with NDoc, which I am not as familiar with, but will have to check out sometime (I currently like Doxygen since it is useful for a lot of languages).
All in all, a good book that deals a lot more with the entire job of tools development, which is somewhat unique in terms of computer books. I realize that for some people (and companies) the need for tools is not quite so apparent. The best analogy I can think of is that tools are the "scaffolding" in which you can build up any project. They solve the simple problems, help tackle the larger problems, glue together what needs to be connected, and improve the productivity of the entire department. Without scaffolding tools everyone would be trying to add another floor to a building by hanging out a window, and if that is the case, you are not going to be able to build quite so high...
Keep reaching for the top,
Michael Hubbard
http://michaelhubbard.ca
The book begin largely on the politics of tools and maintenance and different stakeholders in the tools, as well as some examples of different environments in which tools are created and maintained. There are a few examples of professional studios, who focus on elaborate GUI systems, and WYSISYG (what you see is what you get) world editors.
From there, the book has a chapter on why to create tools with C# with .Net (which is the language and framework used throughout the book). While I agree that .Net can be a great and quick framework for developing solid tools quickly, I would recommend sticking with the language that is closest to the game engine or 3D application whenever possible (since it is often the case that some parts (or the entire tool) can be merged into the game code, and it is better to not have too many languages in any production environment (if possible)).
There is a lot of focus on coding standards, design and documentation which is extremely important for tools, since they are often written specifically for one project, and can be very difficult to port to other projects if the design and standards are poor. Plus, the fact that many tools are written once and then the programmer moves on to something else, it is easier to forget why the code was written a certain way when come back to it in six months. Having a good design and (up-to-date) documentation is very important if no one is going to be looking at the code until the next project, or something breaks. There is some discussion of code metrics, UI design, using NUnit for unit testing, Microsoft coding conventions and using FxCop to enforce coding policies (I also like StyleCop for coding standards). The coding documentation is with NDoc, which I am not as familiar with, but will have to check out sometime (I currently like Doxygen since it is useful for a lot of languages).
All in all, a good book that deals a lot more with the entire job of tools development, which is somewhat unique in terms of computer books. I realize that for some people (and companies) the need for tools is not quite so apparent. The best analogy I can think of is that tools are the "scaffolding" in which you can build up any project. They solve the simple problems, help tackle the larger problems, glue together what needs to be connected, and improve the productivity of the entire department. Without scaffolding tools everyone would be trying to add another floor to a building by hanging out a window, and if that is the case, you are not going to be able to build quite so high...
Keep reaching for the top,
Michael Hubbard
http://michaelhubbard.ca
Saturday, April 16, 2011
Full Indie (Game Dev Event)
In order to keep up with some of the game development stuff, I decided to attend Vancouver Game Indie community (Full Indie) demo night: http://www.fullindie.com/2011/04/12/april-event-game-demo-night-rocked/ It was interesting to see what Vancouver had in the way of indie developers, and looked like there was some good stuff. I suspect that a large percentage of the community was students, but it is always interesting to see what the professional indie developers are up to.
All of the games were interesting, but I especially liked Justin Smith's No Brakes Valet, which is a flash game he created at a game jam, in which you attempt to park cars in different parking stalls, but occasionally there will be some with "no brakes" which makes the car crash into things. He also had a good quote about this: "if you are good at the game, it should be fun, if you are bad at it, it should be fun" (paraphrase). This is good advice for any game designer, and was especially true in his game when the cars banged into each other (without brakes) and caused the other cars to bounce around making a really enjoyable mess. Overall a good experience and look forward to seeing what else it out there (who knows, maybe I will get a chance to contribute something sometime in the future).
Cheers,
Michael Hubbard
http://michaelhubbard.ca
All of the games were interesting, but I especially liked Justin Smith's No Brakes Valet, which is a flash game he created at a game jam, in which you attempt to park cars in different parking stalls, but occasionally there will be some with "no brakes" which makes the car crash into things. He also had a good quote about this: "if you are good at the game, it should be fun, if you are bad at it, it should be fun" (paraphrase). This is good advice for any game designer, and was especially true in his game when the cars banged into each other (without brakes) and caused the other cars to bounce around making a really enjoyable mess. Overall a good experience and look forward to seeing what else it out there (who knows, maybe I will get a chance to contribute something sometime in the future).
Cheers,
Michael Hubbard
http://michaelhubbard.ca
Subscribe to:
Posts (Atom)