{"id":12210,"date":"2011-07-03T09:23:39","date_gmt":"2011-07-03T14:23:39","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=12210"},"modified":"2011-07-03T11:30:23","modified_gmt":"2011-07-03T16:30:23","slug":"project-frontier-12-character-building-exercise","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=12210","title":{"rendered":"Project Frontier #12: Character Building Exercise"},"content":{"rendered":"<p>No pretty screenshots today.  Just long walls of densely-packed text.  Suck it up. We can&#8217;t work on bling-mapping <em>every<\/em> day.<\/p>\n<p>I&#8217;ve mentioned that this was the big hurdle in my project.  This is where I&#8217;ve been planning to run aground and give up. Two two or three times in the past I&#8217;ve tried to nail this down, only to get lost and frustrated, and eventually give up.  <\/p>\n<p>An animated character is a seriously complex beast. There are a lot of steps, all of them are hard, and a mistake in any of them will mangle the end product.  The process goes something like this:<\/p>\n<p><!--more--><\/p>\n<ol>\n<li>Pick a 3D file format out of the dozens available. Collada files? 3DS Max files? Maya? .X? No file format is perfect.  Some are harder to read than others.  (And some are absolute <em>devils<\/em> to read correctly.) Some have acceptable documentation.  Some are proprietary file formats that have been (partly) reverse-engineered by enterprising go-getters. Some have only partial or conflicting documentation for how to read them. There are a lot of different types of information that you will need, and a lot of information you <em>won&#8217;t<\/em> need.  Surface normals, texture mapping coords, animation data, vertex weighting, surface materials &#038; lighting properties.  Some files include non-model stuff like the position of lights and the background you&#8217;re using in your 3D program.  You need to find a format that is as easy to read as possible, has the data you need, and has as little extra stuff as possible.\n<\/li>\n<li>With your file format all picked out, you have to write an importer.  Read that data into your program, convert it from its native coordinate system into the one used by your game, and turn it into polygons for rendering.\n<\/li>\n<li>Pick out an animation file format.  This is a file that will describe the movements for your character.  These poses will describe how to make your character walk, hold a gun, crouch behind chest-high walls, crouch over the face of fallen enemies, or whatever else your characters need to be able to do.  (Although apparently those are the only things anyone cares about?)  Choosing this file format is a lot like the process in step 1.  (Some formats support both models and animations, but you still have to read in this other data.  And using the same files for both comes with its own set of problems, which I&#8217;ll talk about later.)\n<\/li>\n<li>Write an importer for that animation data.\n<\/li>\n<li>Write a system to apply those animations to the model.  You need to be able to smooth animation frames, blend between animations, play animations back at varying speeds, and a bunch of other stuff.  The animations move the character&#8217;s skeleton.  The skeleton moves the vertices, which move the polygons, which makes the magic happen.\n<\/li>\n<li>Now fire up your program.  Doesn&#8217;t work, does it? The limbs are flailing around randomly.  Is it a problem with your model importer? A problem with the animation importer? A problem translating from the foreign coordinate system to your own?  A discrepancy preventing the two from properly relating to one another?  Or perhaps your understanding of the animations, and both file formats is perfect, but you just have a plain old bug in your implementation?  Congratulations. Have fun fishing around in those 2,000 lines of code, looking for a problem that you might not even be able to spot if I pointed to it.  You are well and truly screwed.\n<\/li>\n<\/ol>\n<p>Previously, this was how I tried to do it, which is why I failed. I&#8217;m taking a somewhat more cautious approach this time. This time, I&#8217;m going to work in the opposite direction, starting with the animation system and working my way backwards to importing models. Of course, I can&#8217;t very well develop an animation system without <em>something<\/em> to animate.  So I make this guy:<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier12_1.jpg' class='insetimage'   alt='frontier12_1.jpg' title='frontier12_1.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>A stickman.  (Or, given the rather lopsided ratio of legs to torso, perhaps this is a female? Bah. YOU do better by trying coords in a text editor.) He&#8217;s actually more a collection of points than a proper &#8220;stick&#8221; man.  The lines you see are just there to show the relationship between the points. Each point draws a line to its parent.  I made this thing by hand, using code. Kind of annoying and a slight time sink, but it guaranteed that my skeleton would be exactly as I intended, without needing to write an importer first or wonder if it was working properly. <\/p>\n<p>The next step is to get him moving. I don&#8217;t have an animation system yet, so I&#8217;m just having him rotate his knee on a sine wave.   <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier12_2.jpg' class='insetimage'   alt='frontier12_2.jpg' title='frontier12_2.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>Next, I stick a polygon to him.  This is done by assigning the vertex points to a particular joint.  (People think of skeletons as bones, but in graphics skeletons are really <em>joints<\/em>.  You can see this when you look at models created by different artists.  Some name their skeleton parts, upperarm, forearm, hand.  Others would name those same parts shoulder, elbow, and wrist. Neither one is technically wrong, as long as everyone agrees on how things should be named.) Roughly, this process boils down to me saying, &#8220;This vertex belongs to the knee joint.  When the knee bends, this vertex needs to pivot in space around the knee.&#8221;  <\/p>\n<p>It works.  If I bend the knee, the polygon moves in space with it, making it look like the polygon is &#8220;attached&#8221; to the bone. If I move the hip, it rotates around the hip properly.  <\/p>\n<div class=\"dmnotes\">Incidentally, the skeleton is posed like this for a reason.  This is called the &#8220;T pose&#8221;, and the vast majority of characters are designed in this position. See, if the arms were at his sides, then from the profile view the artist would end up looking through the wireframe of the torso AND both arms.  Having done this myself in the 90&#8217;s, I can tell you it is a horrifying mess to zoom way in to work on one hand and be confronted with a tangle of points from elsewhere on the body.  So artist builds her figure in the T pose, and then an animation is used to make the character lower its arms.  Yes, those people standing rigidly at attention in Oblivion are actually performing an animation, even if all it does it lower their arms. <\/p>\n<p>This should also explain the bug you may see in videogames once in a long while:  You&#8217;ll see this brief glimpse of a character standing arms-out, feet slightly spread. That&#8217;s a character that hasn&#8217;t had any animation applied.<\/p><\/div>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier12_3.jpg' class='insetimage'   alt='frontier12_3.jpg' title='frontier12_3.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>I add a few more polygons, placing them right where his bones would be. I apply a bunch of sine waves to all the joints, and he begins bending and twisting in space.  All the polygons go where they should.  None drift off or pivot around the wrong point. <\/p>\n<p>It&#8217;s stupid, ugly, and primitive, but the important thing is that it is <em>correct<\/em>.  Now I can make an importer.  When things go wrong (and they will go wrong) I&#8217;ll know it&#8217;s a problem with the importer, secure in the knowledge that the underlying animation stuff is correct.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>No pretty screenshots today. Just long walls of densely-packed text. Suck it up. We can&#8217;t work on bling-mapping every day. I&#8217;ve mentioned that this was the big hurdle in my project. This is where I&#8217;ve been planning to run aground and give up. Two two or three times in the past I&#8217;ve tried to nail [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[66],"tags":[],"class_list":["post-12210","post","type-post","status-publish","format-standard","hentry","category-programming"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/12210","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=12210"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/12210\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=12210"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=12210"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=12210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}