| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988 |
- <html xmlns:MSHelp="http://msdn.microsoft.com/mshelp" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xanx="http://schemas.microsoft.com/developer/xanx/2005"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="save" content="history" /><title>Normal Mapping Effect Sample</title>
- <style><!--
- /***********************************************************
- * SCRIPT-SUPPORTING STYLES
- ***********************************************************/
- /* Defines the userData cache persistence mechanism. */
- .userDataStyle
- {
- behavior: url(#default#userData);
- }
- /* Used to save the scroll bar position when navigating away from a page. */
- div.saveHistory
- {
- behavior: url(#default#saveHistory);
- }
- /* Formats the expand/collapse images for all collapsible regions. */
- img.toggle
- {
- border: 0;
- margin-right: 5;
- }
- /* Formats the Language filter drop-down image. */
- img#languageFilterImage
- {
- border: 0;
- margin-left: 0;
- vertical-align: middle;
- }
- /* Formats the Members Options filter drop-down image. */
- img#membersOptionsFilterImage
- {
- border: 0;
- margin-left: 0;
- vertical-align: middle;
- }
- /* Formats the Collapse All/Expand All images. */
- img#toggleAllImage
- {
- margin-left: 0;
- vertical-align: middle;
- }
- /* Supports XLinks */
- MSHelp\:link
- {
- text-decoration: underline;
- color: #0000ff;
- hoverColor: #3366ff;
- filterString: ;
- }
- body
- {
- background: #FFFFFF;
- color: #000000;
- font-family: Verdana;
- font-size: medium;
- font-style: normal;
- font-weight: normal;
- margin-top: 0;
- margin-bottom: 0;
- margin-left: 0;
- margin-right: 0;
- width: 100%;
- /*font-size: 110%;*/
- }
- div.section
- {
- margin-left: 15px;
- }
- div.hxnx5
- {
- margin-left: 1.5em;
- }
- /* Font for all headings */
- h1, h2, h3, h4, h5, h6
- {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- margin-top: 18;
- margin-bottom: 8;
- font-weight: bold;
- }
- h1
- {
- font-size: 130%;
- color: #003399;
- }
- div#scrollyes h1 /* Changes font size for full-scrolling topic */
- {
- font-size: 150%;
- }
- h2
- {
- font-size: 122%;
- }
- h3
- {
- font-size: 115%;
- margin-top: 9;
- margin-bottom: 4;
- }
- h4
- {
- font-size: 115%;
- margin-top: 9;
- margin-bottom: 4;
- }
- h5
- {
- font-size: 100%;
- margin-top: 9;
- margin-bottom: 4;
- }
- h6
- {
- font-size: 100%;
- margin-top: 9;
- margin-bottom: 4;
- }
- ul p, ol p, dl p
- {
- margin-left: 0em;
- }
- p
- {
- margin-top: .6em;
- margin-bottom: .6em;
- }
-
- td p
- {
- margin-top: 0.0em;
- margin-bottom: 0.6em;
- }
- dd p
- {
- margin-top: 0.0em;
- margin-bottom: 0.6em;
- }
- .image
- {
- text-align: center;
- }
- dl
- {
- margin-top: 0em;
- margin-bottom: 1.3em;
- }
- dd
- {
- margin-bottom: 0em;
- margin-left: 1.5em;
- }
- dl.glossary dd
- {
- margin-bottom: 0em;
- margin-left: 1.5em;
- }
- dt
- {
- margin-top: .6em;
- margin-bottom: 1;
- }
- ul, ol
- {
- margin-top: 0.6em;
- margin-bottom: 0.6em;
- }
-
- ol
- {
- margin-left: 2.5em;
- }
-
- ul
- {
- list-style-type: disc;
- margin-left: 1.9em;
- }
- li
- {
- margin-bottom: 0.4em;
- }
- ul ol, ol ol
- {
- list-style-type: lower-alpha;
- }
- pre
- {
- margin-top: .6em;
- margin-bottom: .6em;
- font: 105% Lucida, mono;
- color: #000066;
- }
- code
- {
- font-family: Monospace, Courier New, Courier;
- font-size: 105%;
- color: #000066;
- }
- table.userdata td
- {
- background: #ffffff;
- background-color: #F5F5F5;
- border-color: #ffffff;
- border: none;
- }
- table.clsWarning
- {
- background: #ffffff;
- padding: 0px;
- margin: 0px;
- border: none;
- }
- table.clsWarning td
- {
- padding: 0px;
- margin: 0px;
- background: #ffffff;
- vertical-align: middle;
- font-size: 70%;
- }
- div#mainSection table
- {
- width: 95%;
- background: #ffffff;
- margin-top: 5px;
- margin-bottom: 5px;
- }
- div#mainSection table th
- {
- padding: 5px 6px;
- background: #EFEFF7;
- text-align: left;
- font-size: 70%;
- vertical-align: bottom;
- border-bottom: 1px solid #C8CDDE;
- }
- div#mainSection table td
- {
- padding: 5px 5px;
- background: #F7F7FF;
- vertical-align: top;
- font-size: 70%;
- border-bottom: 1px solid #D5D5D3;
- }
- div#syntaxCodeBlocks table th
- {
- padding: 1px 6px;
- color: #000066;
- }
- div#syntaxCodeBlocks table td
- {
- padding: 1px 5px;
- }
- /* Applies to the running header text in the first row of the upper table in the
- non-scrolling header region. */
- span#runningHeaderText
- {
- color: #003399;
- font-size: 90%;
- padding-left: 13;
- }
- /* Applies to the topic title in the second row of the upper table in the
- non-scrolling header region. */
- span#nsrTitle
- {
- color: #003399;
- font-size: 120%;
- font-weight: 600;
- padding-left: 13;
- }
- /* Applies to everything below the non-scrolling header region. */
- div#mainSection
- {
- font-size: 70%;
- width: 100%;
- }
- /* Applies to everything below the non-scrolling header region, minus the footer. */
- div#mainBody
- {
- font-size: 90%;
- margin-left: 15;
- margin-top: 10;
- padding-bottom: 20;
- }
- /* Adds right padding for all blocks in mainBody */
- div#mainBody p, div#mainBody ol, div#mainBody ul, div#mainBody dl
- {
- padding-right: 5;
- }
- div#mainBody div.alert, div#mainBody div.code, div#mainBody div.tableSection
- {
- width:98.9%;
- }
- div.alert p, div.code p
- {
- margin-top:5;
- margin-bottom:8;
- }
- /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Begin Note Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
- div#mainSection div.alert table
- {
- border: 0;
- }
- div#mainSection div.alert table th
- {
- padding-top: 0;
- padding-bottom: 0;
- padding-left: 5;
- padding-right: 5;
- }
- div#mainSection div.alert table td
- {
- padding-left: 5;
- padding-right: 5;
- }
- img.note
- {
- border: 0;
- margin-left: 0;
- margin-right: 3;
- }
- /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - End Note Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
- /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Begin Non-scrolling Header Region Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
- /* Applies to the entire non-scrolling header region. */
- div#header
- {
- background-color: #D4DFFF;
- padding-top: 0;
- padding-bottom: 0;
- padding-left: 0;
- padding-right: 0;
- width: 100%;
- }
- /* Applies to both tables in the non-scrolling header region. */
- div#header table
- {
- margin-top: 0;
- margin-bottom: 0;
- border-bottom-color: #C8CDDE;
- border-bottom-style: solid;
- border-bottom-width: 1;
- background: #D4DFFF;
- width: 100%;
- }
- /* Applies to cells in both tables in the non-scrolling header region. */
- div#header table td
- {
- color: #0000FF;
- font-size: 70%;
- padding-right: 20;
- padding-top: 1;
- padding-bottom: 1;
- border: none;
- background: #D4DFFF;
- }
- /* Applies to the last row in the upper table of the non-scrolling header region. Text
- in this row includes See Also, Constructors, Methods, and Properties. */
- div#header table tr#headerTableRow3 td
- {
- padding-bottom: 2;
- padding-top: 5;
- padding-left: 15;
- }
- /* Applies to the lower table in the non-scrolling header region. Text in this table
- includes Collapse All/Expand All, Language Filter, and Members Options. */
- div#header table#bottomTable
- {
- border-top-color: #FFFFFF;
- border-top-style: solid;
- border-top-width: 1;
- text-align: left;
- padding-left: 15;
- }
- blockquote
- {
- margin-left: 3.8em;
- margin-right: 3.8em;
- margin-top: .6em;
- margin-bottom: .6em;
- }
- sup
- {
- text-decoration: none;
- font-size: smaller;
- }
- a:link
- {
- color: #0000FF;
- /* font-weight: bold */
- }
-
- a:visited
- {
- color: #0000AA;
- /* font-weight: bold */
- }
-
- a:hover
- {
- color: #3366FF;
- /* font-weight: bold */
- }
-
- .label
- {
- font-weight: bold;
- margin-top: 1em;
- margin-left: -26px;
- }
-
- .tl
- {
- margin-bottom: .75em;
- }
-
- .atl
- {
- padding-left: 1.5em;
- padding-bottom: .75em;
- }
-
- .cfe
- {
- font-weight: bold;
- }
-
- .mini
- {
- font-size: smaller;
- }
-
- .dt
- {
- margin-bottom: -.6em;
- }
-
- .indent
- {
- margin-left: 1.9em;
- margin-right: 1.9em;
- }
- .product
- {
- text-align: right;
- color: #333333;
- font-size: smaller;
- font-style: italic;
- }
- .buttonbarshade
- {
- position: relative;
- margin: 0;
- left: 0px;
- top: 2;
- width: 50%;
- height: 40px;
- }
- .buttonbartable
- {
- position: absolute;
- margin: 0;
- padding:0;
- border:0;
- left:0px;
- top: 2;
- width: 100%;
- height: 40px;
- }
- /* background color, font for header */
- table.buttonbartable td, table.buttonbarshade td
- {
- background: #ffffff; /*#5177B8; #80C615;*/
- border-left: 0px solid #80C615;
- margin: 0;
- padding: 0px 0px 0px 0px;
- font-family: Impact, sans-serif;
- font-size: 14pt;
- }
- table.buttonbartable td.button1
- {
- background: #5177B8; /*#80C615;*/;
- padding: 0;
- font-weight: bold;
- text-align: center;
- cursor: hand;
- }
- table.buttonbartable td.button2
- {
- background: #5177B8; /*#80C615;*/;
- font-weight: bold;
- text-align: center;
- }
- table.buttonbartable td.button3
- {
- background: #5177B8; /*#80C615;*/;
- font-weight: bold;
- text-align: center;
- }
- table.buttonbartable td.runninghead
- {
- padding-left: 0px;
- font-style: italic;
- text-align: left;
- }
- .version
- {
- text-align: left;
- color: #000000;
- margin-top: 3em;
- margin-left: -26px;
- font-size: smaller;
- font-style: italic;
- }
- .lang, .ilang
- {
- color: #0000ff;
- font: normal 7pt Arial, Helvetica, sans-serif;
- }
- div.langMenu
- {
- position: absolute;
- z-index: 1;
- width: 96pt;
- padding: 8pt;
- visibility: hidden;
- border: 1px solid #000000;
- background: #ffffd0;
- }
- div.langMenu ul
- {
- padding-left: 2em;
- margin-left: 0;
- }
- div.filtered
- {
- margin: 4pt 0 8pt -26px;
- padding: 4px 4px 8px 26px;
- width: 100%;
- border: 2px solid #aaaacc;
- background: #ffffff;
- }
- div.filtered2
- {
- margin: 4pt 0 8pt -26px;
- padding: 4px 4px 8px 26px;
- width: 100%;
- border: none;
- background: #ffffff;
- }
- div.filtered h1, div.filtered h2, div.filtered h3, div.filtered h4
- {
- margin-left: -22px;
- }
- div.filtered span.lang
- {
- position: relative;
- left: -22px;
- }
- div.reftip
- {
- position: absolute;
- z-index: 1;
- padding: 8pt;
- visibility: hidden;
- border: 1px solid #000000;
- background: #ffffd0;
- }
- a.synParam
- {
- color: #0000FF;
- /*color: #3F7800;*/
- /*color: #8DC54F;*/
- text-decoration: none;
- font-weight: normal;
- }
- a.synParam:hover
- {
- text-decoration: underline;
- font-weight: normal;
- }
- div.sapop
- {
- position: absolute;
- z-index: 1;
- left: 26px;
- width: 100%;
- padding: 10px 10px 10px 36px;
- visibility: hidden;
- border: 1px solid #000000;
- background: #ffffd0;
- }
- div.footer
- {
- width: 100%;
- border: none;
- background: #ffffff;
- margin-top: 18pt;
- padding-bottom: 12pt;
- color: #0000FF;
- /*color: #228B22; */
- text-align: center;
- font-size: 76%;
- }
- div.preliminary
- {
- margin-top: 8pt;
- padding-bottom: 12pt;
- color: #A0A0A0;
- }
- /* A procedure section. eg. 'To create a file', 'To add a value' */
- div.proc
- {
- margin-left: 0.5em;
- }
-
- /* The title of a 'procedure' section. */
- div.proc h3
- {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-weight: bold;
- font-size: 115%;
- margin-top: 1em;
- margin-bottom: 0.4em;
- margin-left: -0.5em;
- color: #003399;
- }
- div.proc ul
- {
- margin-left: 1.5em;
- }
- div.proc ol
- {
- margin-left: 2.0em;
- }
-
- .note
- {
- margin-left: 14pt;
- margin-right: 12pt;
- }
- .indent1
- {
- margin-left: 12pt;
- }
- .indent2
- {
- margin-left: 24pt;
- }
- .indent3
- {
- margin-left: 36pt;
- }
- p.proch
- {
- padding-left: 16px;
- }
- p.proch img
- {
- position: relative;
- vertical-align: top;
- left: -18px;
- margin-right: -14px;
- margin-bottom: -18px;
- }
-
- div.clsPlatSpec
- {
- background-color:#FFF8DC;
- border-style:solid;
- border-width:1pt 0pt 0pt 1pt;
- border-color:#ffE4C4;
- margin-top:0.6em;
- width:100%;
- }
- /* Applies to the language labels in the Language Filter drop-down list. */
- .languageFilter
- {
- color: #0000FF;
- cursor:hand;
- text-decoration:underline;
- padding-bottom:4;
- }
- /* Dropdown areas */
- #languageSpan {
- position: absolute;
- visibility: hidden;
- border-style: solid;
- border-width: 1px;
- border-color: #C8CDDE;
- background: #d4dfff;
- padding: 4px;
- font-size: 70%;
- }
- #membersOptionsSpan {
- position: absolute;
- visibility: hidden;
- border-style: solid;
- border-width: 1px;
- border-color: #C8CDDE;
- background: #d4dfff;
- padding: 4px;
- font-size: 70%;
- }
- --></style>
- <xml>
- <MSHelp:TOCTitle Title="Normal Mapping Effect Sample" />
- <MSHelp:RLTitle Title="Normal Mapping Effect Sample" />
- <MSHelp:Keyword Index="A" Term="O:Microsoft.Xna.NormalMappingEffect" />
- <MSHelp:Keyword Index="A" Term="f9d93db2-f058-34f5-9102-e106d7f56b4a" />
- <MSHelp:Keyword Index="K" Term="Normal Mapping Effect Sample" />
- <MSHelp:Attr Name="AssetID" Value="f9d93db2-f058-34f5-9102-e106d7f56b4a" />
- <MSHelp:Attr Name="Locale" Value="en-us" />
- <MSHelp:Attr Name="CommunityContent" Value="1" />
- <MSHelp:Attr Name="TopicType" Value="kbOrient" />
- </xml>
- </head><body><div id="mainSection"><div id="mainBody">
- <h1>Normal Mapping Effect Sample</h1>
- <p>
- This sample builds on the concepts illustrated in the Custom Model Effect sample, and applies a normal mapping effect to a mesh. The sample also uses a custom content processor to demonstrate two additional concepts: adding and removing per vertex data from a mesh, and reading opaque data.
- </p>
- <a id="ID2EN" name="ID2EN"> </a><h1 class="heading">Sample Overview</h1><div id="ID2EN" class="hxnx1">
-
- <p>The sample demonstrates how to use a custom effect to render a model with a normal map, creating the appearance of a bumpy surface without needing to render additional geometry.</p>
- <p>A custom content pipeline processor applies the normal mapping effect to the model during the content build process. The processor also creates additional per-vertex data channels for the binormal and tangent information, which the normal mapping effect uses. Finally, the processor uses opaque data to determine which normal map to use, and then applies it to the material.</p>
- <p>To make rendering more efficient at run time, a second custom processor changes the normal map pixel format from an encoded unsigned format to a signed format.</p>
- <a id="ID2EX" name="ID2EX"> </a><h2 class="subHeading">Sample Controls</h2><div id="ID2EX" class="hxnx2">
-
- <p>This sample uses the following keyboard and gamepad controls.</p>
- <table>
- <tr>
- <th>Action</th>
- <th>Keyboard control</th>
- <th>Gamepad control</th>
- </tr>
- <tr>
- <td>Rotate the model</td>
- <td>
- <p>UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW or W, S, A, and D</p>
- </td>
- <td>Right thumb stick</td>
- </tr>
- <tr>
- <td>Zoom</td>
- <td>Z and X</td>
- <td>Triggers</td>
- </tr>
- <tr>
- <td>Pause or resume light animation.</td>
- <td>SPACEBAR</td>
- <td>
- <b>A</b>
- </td>
- </tr>
- <tr>
- <td>Reset.</td>
- <td>R</td>
- <td>Right thumb stick press</td>
- </tr>
- <tr>
- <td>Exit.</td>
- <td>ESC or ALT+F4</td>
- <td>
- <b>BACK</b>
- </td>
- </tr>
- </table>
- </div>
- </div>
- <a id="ID2EBD" name="ID2EBD"> </a><h1 class="heading">How the Sample Works</h1><div id="ID2EBD" class="hxnx1">
-
- <p>
- The <b>NormalMappingModelProcessor</b> derives from the built-in <b>ModelProcessor</b>, and overrides the <b>ConvertMaterial</b> method to pass all the materials on the model through to the custom <b>NormalMappingMaterialProcessor</b>.
- </p>
- <p>
- Additionally, the processor performs preprocessing on the vertex channels in the scene.
- The processor overrides the <b>GenerateTangentFrames</b> property, forcing it to always return true. This causes the base
- <b>ModelProcessor</b> to generate tangent and binormal information for a mesh. The normal mapping technique depends
- on the presence of this data. The processor also overrides <b>ProcessVertexChannel</b> to remove vertex channels
- that the normal mapping vertex shader does not use. This optimization makes the run-time vertex buffers
- smaller and makes them draw more quickly.
- </p>
- <p>
- The last functionality that the processor provides is to determine the normal map texture to use in the effect.
- The value could be hard-coded in the processor—as NormalMap.tga, for example—but this would not be
- a very flexible approach. The processor would work only in extremely simple cases. Instead, the processor uses
- opaque data to determine the path to the texture. Opaque data, also known as blind data and custom attributes,
- is data that can be applied to a mesh in a digital content creation (DCC) tool. 3ds Max, Maya, and the FBX file format support opaque data. The FBX importer that ships with XNA Game Studio reads this data and
- puts it into an <b>OpaqueDataDictionary</b>, which is a
- map from names to objects.
- </p>
- <p>
- For this sample, the artist created the lizard and rock in Maya. Before exporting to FBX, a custom attribute
- was applied to all meshes in the scene. The custom attribute is named <b>NormalMap</b>, and its
- value is a string containing the path to the texture, such as lizard_norm.tga, or rock_norm.tga. The processor
- looks for this attribute in every mesh's <b>OpaqueData</b> property,
- and adds the texture file that is found to the collection of textures to be processed. This approach is preferable to
- hard coding the file name in the processor, because it works on any number of meshes in a scene, and it gives control to the artist.
- </p>
- <p>
- To accomplish this, the function <b>LookUpNormalMapAndAddToTextures</b> is used to recurse through the scene hierarchy.
- Whenever a <b>MeshContent</b> object is found, the processor looks in that mesh's opaque data to find the normal map.
- </p>
- <p>
- The processor also provides a parameter that the user can set in the UI to "override" for the opaque data.
- If a value is specified in the property grid for <b>NormalMapTexture</b>, that value will be used as the normal map
- for all meshes, overriding what is specified in the opaque data. This is useful when using content-creation tools
- that do not support opaque data. However, this approach forces every mesh in the entire scene to share one normal
- map.
- </p>
- <p>
- The <b>NormalMappingMaterialProcessor</b>, which is used by the <b>NormalMappingModelProcessor</b>, is much simpler. It extends from the built-in <b>MaterialProcessor</b>, and overrides <b>MaterialProcessor.BuildTexture</b> to control the processor that is used to build textures. Normal maps are sent through the custom <b>NormalMapTextureProcessor</b>. All other textures are built as normal.
- </p>
- <p>
- Finally, the <b>NormalMapTextureProcessor</b> is used to process normal map textures. It takes input textures that contain encoded normals with unsigned RGB values ranging from 0 to 1. The textures are converted to the <b>Graphics.PackedVector.NormalizedByte4</b> format, a signed format designed for use with normal maps.
- </p>
- <p>
- Note that the textures and effects are not added to the Game Studio project file. These will be built automatically because they are referenced by the <b>NormalMappingModelProcessor</b>, so there is no need for them to be duplicated in the project itself.
- </p>
- </div>
- <a id="ID2ERF" name="ID2ERF"> </a><h1 class="heading">
- Converting to XNA Game Studio 2.0
- </h1><div id="ID2ERF" class="hxnx1">
-
- <p>
- The most extensive changes to this sample will be in the content processors. Let us cover the quick, cosmetic changes first. In this
- version of the sample, we have modified the <b>NormalMappingModelProcessor</b> <b>ContentProcessorAttribute</b>, giving it a <i>DisplayName</i>. This will make it
- appear more nicely in the property grid.
- </p>
- <p>
- Next, in XNA Game Studio 2.0, processors can be made to not show up in the UI by using the <b>DesignTimeVisibleAttribute</b>. The
- <b>NormalMappingMaterialProcessor</b> and <b>NormalMappingTextureProcessor</b> are ideal candidates for invisiblility because it does not make sense to
- use these processors on their own. They are only helper processors used by the <b>NormalMappingModelProcessor</b>.
- </p>
- <p>
- The final changes revolve around the use of XNA Game Studio 2.0 processor parameters feature. In XNA Game Studio 2.0, content processors can expose parameters
- that change the way they build content. Using those parameters, you can tweak the way that each piece of content is built. For example, the Model
- Processor exposes a scale parameter that can be used to rescale a model without having to edit the source file itself. It is not only built-in
- processors that have this ability, user-defined processors can do this as well.
- </p>
- <p>
- In the first version of this sample, the file name of the normal map was taken from the opaque data on the model. This is a very flexible
- and powerful solution, but not all content creation tools support opaque data. This meant, unfortunately, that they could not create data that would be compatible with this sample.
- </p>
- <p>
- Now that we have processor parameters, however, the story is a little different! By giving our <b>NormalMappingModelProcessor</b> a parameter
- of type string, we can give the users of our processor a way to specify the normal map without using opaque data. This is not the ideal solution because it will force all meshes in the scene to share the same normal map; for some users this may be good enough.
- </p>
- <p>
- Defining a parameter on a processor is easy: simply create a property on the processor with a public getter and setter. We have defined one
- on <b>NormalMappingModelProcessor</b> called "NormalMapTexture." Additionally, we can use attributes to feed some more information about our parameter
- to the UI. Specifically, we use <b>System.ComponentModel.DisplayNameAttribute</b> to control the name of the property in the UI,
- <b>System.ComponentModel.DescriptionAttribute</b> to control the description, and <b>System.ComponentModel.DefaultValueAttribute</b> to control the default
- value. Note that the default value specified in this attribute is only a hint to the UI, and is not used when building the content.
- </p>
- <p>
- Once the parameter has been defined, it can be used in the property grid. When the asset is built and the processor's Process method
- is called, the property's setter will be invoked and will be set to the correct value.
- </p>
- <p>
- While we are working with processor parameters, let us discuss another change in the <b>NormalMappingModelProcessor</b>. This processor inherits
- from <b>ModelProcessor</b>, which has been updated in XNA Game Studio 2.0 to have several parameters, including the scale parameter mentioned earlier. One
- of these in particular is interesting to us: <b>GenerateTangentFrames</b>. Tangent frames are a necessary piece of data to implement normal
- mapping. In the original version of this sample, we generated this data using <b>MeshHelper.CalculateTangentFrames</b>. Now, the base <b>ModelProcessor</b>
- can do this for us! To tell it to do so, we will override its <b>GenerateTangentFrames</b> property. Because this data is required for normal mapping
- and should not be optional, we will have the getter always return true and the setter do nothing. Also, we will use
- <b>System.ComponentModel.BrowsableAttribute</b> to specify to the UI that this property should not be displayed.
- </p>
- <p>
- Because the base <b>ModelProcessor</b> is generating tangent frames for us, we no longer need to do so in <b>PreprocessSceneHierarchy</b>. Removing
- <b>CalculateTangentFrames</b> from <b>PreprocessSceneHierarchy</b> leaves it looking rather bare: its only job is to recursively peruse the scene
- <b>LookUpNormalMapAndAddToTextures</b>. Instead of doing this, we just make <b>LookUpNormalMapAndAddToTextures</b> a recursive function and
- remove <b>PreprocessSceneHierarchy</b> entirely. In the process, we update the function <b>LookUpNormalMapAndAddToTextures</b> to use the user's
- value for <b>NormalMapTexture</b>, if one is specified.
- </p>
- </div>
- </div><div class="footer" id="footer"><p>© 2010 Microsoft Corporation. All rights reserved.<br />Send feedback to <a href="mailto:[email protected]?subject=Documentation Feedback: Normal Mapping Effect Sample">[email protected]</a>.</p></div></div></body></html>
|