Tuesday, December 29, 2009

OUT OF OFFICE

Hey guys, I will be "out of office" from today for a week or so.

For those of you who usually read this blog thanks a lot for you time and comments.

Don’t forget to check the new “Developer Talk Series” –by members of the XNA Game Studio Product Team, starting on January 2010!!

See you next year ;)

Happy holidays and cheers!
~Pete

> Link to Spanish version.

Tuesday, December 08, 2009

PDC09 VIDEOS AVAILABLE

The videos showing all PDC09’s presentations are now available either for online watch or download.

In particular, for those interested in using DX11 to develop .NET applications & games for Windows 7, check these videos out:

Enjoy,
~Pete

> Link to Spanish version.

Monday, November 30, 2009

RAIMI WANTS A “PANIC ATTACK”

Do you guys remember how Montevideo was attacked by an horde of giant robots and high-tech fighters?

Well, according to many news that have been recently posted around the globe, Sam Raimi’s contacted the authors of this amazing short-film in order to convert it into a whole movie.

Well done, guys! You really deserve it …

Cheers!
~Pete

> Link to Spanish version.

Wednesday, November 25, 2009

WATCH OUT THE XBOX 360 YOU BUY!

It’s been a little while since my last post; well … there’s a wonderful reason for that: last week my first son was born! So, I had little-to-none time left for blogging.

Ok, down to business now …

In the past few weeks you may have heard and or witnessed that a new update for the XBox 360 was recently released, bringing a bunch of new exciting features like Twitter, Facebook and Last.fm support.

You may have also heard that Microsoft banned unofficial memory units.

Well, that is not what I’m going to blog about today!

There is one more thing you may have heard (and I hope you haven’t experienced yourself): MSFT has banned a plethora of (modified) consoles (the buzz is that the number of bans is close to one million consoles).

The reason? Simple: fighting against piracy.

Thus, if -for whatever reason- you hacked the disc drive, installed unauthorized software, created a modded version of the console, and –maybe in some cases- repaired it yourself from the RROD with a custom solution, then you are likely to lay within the unlucky number!

What does a banned 360 mean? Simply put: the XBox Live service is not available any longer to that console. The owner can still play offline, but forget about Arcade and Indie games.

And since the gamertag/account is not banned, just the console, that gamer can purchase a new console to regain access to those channels. What brings me to the main topic of this post:

WATCH OUT THE XBOX 360 CONSOLE YOU BUY!!!

As a result of the massive banning process, many owners of banned 360s are trying to sell those consoles through popular online auction sites, claiming that they never experienced technical problems, offering a bunch of (pirated) games, and not even mentioning that console is “modded” in the first place.

Always remember: the ban is on the console itself, so if you happen to buy a banned 360, it doesn’t matter that you have a valid gamertag / account, the ban on the console won’t disappear.

So, be careful on the 360 you pick: verify it’s a brand new one on a sealed (never opened) pack before closing any deal.

And don’t install pirate software: is illegal, and eventually -as this wave of bans prove, it turns to be more expensive than doing things right in the first place!

Play safe,
~Pete

> Link to Spanish version.

Friday, November 06, 2009

MONTEVIDEO INVADED BY GIANT ROBOTS

Browsing the web I happened to find today a quite impressive CG short film, where the city of Montevideo, capital of Uruguay (where I live), is being suddenly attacked by an army of giant robots and a squad of pretty advanced space fighters.

The film is entitled “Atáque de Pánico! 2009” (“Panic Attack! 2009”) and you can find more info about it here.

Kudos to the producers for this great release!

Enjoy,
~Pete

[If you ask me, the title is kinda misnomer. I would have chosen a different one for such a great production. What do you guys think?]

> Link to Spanish version.

RULES FOR MULTIPLE LANGUAGES IN XBLIG

Recently, there’s been some fuss regarding the use of (symbols from) unsupported languages in Xbox Live Indie Games (in particular, traditional Mah-jong symbols).

This discussion led the XNA Team to post the following two messages to clarify things:

Read both messages carefully and if you still have questions post them in or browse the forums to get the answers.

‘till next time,
~Pete

 

> Link to Spanish version.

Saturday, October 24, 2009

NEW XBLIG BARGAINS!

As of today and on, the only prices available for XBox Live Indie Games will be 80, 240 and 400 points (that is in American dollars: one, three and five, respectively).

As a result of this mandatory switch, all games that were offered for 200 points are now selling at the bargain price of 80 points … 1 dollar!

On Gamerbytes, you can find info about some of the games selling at $1 and or you can also visit XBLA & XBLIG Ratings for a complete list of the games being offered for this price.

Time to get lots of bargains!!!

Enjoy!
~Pete

 

> Link to Spanish version.

Thursday, October 22, 2009

TWO NEW SAMPLES AT XNA CCO

You may be aware of this news, but just in case, I want to mention that two great new samples are now available on the site of XNA Creators Club:

Now, what’s great about them? Glad you’ve asked.

(I) Skinned Model Extension Sample

The first sample shows you how to extend the original Skinned Model sample so that:

  • You can “move” a part of a skinned model independently of an animation sequence (like in this case, the arm and or head),
  • You can position objects relative to a specific bone (in this case, a bat), and
  • You can properly attach bounding geometry to the animated model (in this case, spheres).

The code is a great source of technical knowledge regarding the skinning technique for programmers but, thanks to some minor glitches in the skinned model, they can also learn how cumbersome and picky are the processes of (not only modeling and unwrapping but) rigging, skinning and animating a 3D model, so that it looks ok in each and every (expected) situation before going gold.

First screenshot:

As you can see here, when you move the head one of the vertices remains (almost) still, which means that either it wasn’t attached to the neck's bone at all or, if it was, then its “weights” should be readjusted.

Second screenshot:

It seems that the part of the belt marked above in orange moves along with the hip whilst the rest of the belt moves with the torso. And so, the walking animation, even if it may behave as expected, looks kinda weird.

This is likely not relevant for programmers -being the programming part the only/main purpose of the sample itself, but for indies like me that tend to develop all parts of a game “in solo mode”, its a fabulous example of some of the headaches they are going to deal with on the artistic side of things.

(II) Primitives 3D Sample

The second one presents the set of classes you will need to create a cube, a sphere, a cylinder, a torus and the famous teapot!

These primitives are usually found in most famous modeling apps, among others, like 3D Studio Max or Maya.

You may wonder: why the teapot is considered a primitive? Well, thanks to the type of powerful operations used to create it, procedurally: bezier-curve calculation! Nice one, indeed …

One suggestion for the cylinder:

In case some of you want to create the triangles that conform each cap of the cylinder so that they lay out uniformly around a centered vertex, instead of towards one side -as shown above, then you’ll have to replace the following method in the “CylinderPrimitive” class:

/// <summary>
/// Helper method creates a triangle fan to close the ends of the cylinder.
/// </summary>
void CreateCap( int tessellation, float height, float radius, Vector3 normal )
{
  // Create cap indices.
  for ( int i = 0; i < tessellation - 2; i++ )
  {
    if ( normal.Y > 0 )
    {
      AddIndex( CurrentVertex );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
    }
    else
    {
      AddIndex( CurrentVertex );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
    }
  }
 
  // Create cap vertices.
  for ( int i = 0; i < tessellation; i++ )
  {
    Vector3 position = GetCircleVector( i, tessellation ) * radius +
                       normal * height;
 
    AddVertex( position, normal );
  }
}

So that, instead, it looks similar to:

/// <summary>
/// Helper method that creates a cap laying out triangles around a centered vertex.
/// </summary>
void CreateCap( int tessellation, float height, float radius, Vector3 normal )
{
  // Add a vertex in the center of the cap.
  AddVertex( normal * height, normal );
 
  // Create cap indices, taking into account the centered vertex (that is why
  // we use "CurrentVertex - 1" as the first element of the corresponding index).
  for ( int i = 0; i < tessellation; i++ )
  {
    if ( normal.Y > 0 )
    {
      AddIndex( CurrentVertex - 1 );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
    }
    else
    {
      AddIndex( CurrentVertex - 1 );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
    }
  }
 
  // Create cap vertices.
  for ( int i = 0; i < tessellation; i++ )
  {
    Vector3 position = GetCircleVector( i, tessellation ) * radius +
                       normal * height;
 
    AddVertex( position, normal );
  }
}

Being the result:

This isn’t critical at all, but for the sake of (better) unwrapping -and even of creating more “sections” (rings) on each cap, programmatically- 2 centered vertices and a few more triangles come in handy without hurting performance.

Code on!
~Pete

 

> Link to Spanish version.

Thursday, October 15, 2009

SNEAK PEEK: 360’S DASHBOARD UPDATE

1UP has published a video showing off the features coming next with the dashboard update of the XBox 360 console; that is:

  • Twitter,
  • Facebook,
  • Last.fm, and
  • Zune Marketplace.

But that’s not it! Major Nelson has posted info on how to sign up for the update preview so as to start enjoying the upcoming features right now.

So, do you have a Xbox 360? And, is the XBox Live service available for the country where you live?

If the answer to both questions is “yes”, then what the heck are you waiting for?!!!

Enjoy,
~Pete

 

> Link to Spanish version.

Wednesday, October 14, 2009

NEW XBOX CONSOLE FOR 2012 ?

Lately, there’s been some buzz regarding the near future of MSFT’s main gaming console.

Words like “XBox 720”, “X-Engine” and “Project Phoenix” have been used. But what do they mean, exactly?

According to this article, “Project Phoenix” is the name internally given by MSFT for what some people outside the Company have called “XBox 720”.

Now, a more recent article states that the new console would hit the markets on 2012, with a new gfx card from ATI so as to maintain backward compatibility as well as boost performance.

Regarding the latter (performance), a new “X” engine would have been recently released, not only including a new set of tools but also “a whole new way to develop for the system” -as this third article says).

I don’t know whether all these “news” are true or not, but it’s an interesting read, though.

Plus, imvho, there’s a lot of “360” yet to enjoy, specially when Project Natal gets out; don’t you think?

Stay tuned,
~Pete

 

> Link to Spanish version.

Wednesday, October 07, 2009

VIRTUAL EVENT: “THE NEW EFFICIENCY”

There’s a very interesting virtual event to be held today. If you either speak and or understand the Spanish language you cannot miss it!

Read on …

Today, Microsoft will present with experts live and demonstrations the new concept of efficiency!

Some of the topics being addressed during the event cover the following technologies/products:

  • Windows 7,
  • Windows Server 2008 R2,
  • Exchange Server 2010, and
  • Fore Front.

So, what are you waiting for? Just go and register for the event a.s.a.p.:

http://www.lanuevaeficiencia.com/

Enjoy!
~Pete

 

> Link to Spanish version.

Thursday, September 24, 2009

WATCHING THE SUN BURN - PART 2

On part 1 of the series, I had introduced the lighting and rendering engine named “Sunburn”, from Synapse Gaming.

Well, version 1.2.4 of the engine is out, bringing a boost in performance for the forward rendering technique.

Hereunder you will find my latest results for the reflection/refraction demo (which uses the above-mentioned technique):

1) PC platform:

  • Min: 37 fps -> watching the three orbs from almost the roof (close to the windows on top),
  • Max: 60 fps -> watching one of the windows on top, and
  • Average: 43 fps -> in general (sometimes a bit more on the corridors, not facing the orbs).

2) XBox 360 console:

  • Min: 28 fps -> same case as the PC platform,
  • Max: 54 fps -> ditto,
  • Average: 32 fps -> ditto.

It’s important to notice that, in this demo, the scene is rendered three times per frame: once for the reflect image, once for the refract image, and once for the final output. Why? In order to allow a dynamic behavior. Meaning? Instead of faking the effect with “static” snapshots, what you see is updated and calculated on real-time; thus, moving objects are caught by the process, refracted and reflected.

Again, for both tests I drew the final image on a high resolution back-buffer of 1280x720, but this time I also run the tests on the XBox 360 (both with a varying time step).

Given those results, I cannot wait to see a version with deferred rendering on the XBox 360!

Now, how difficult is to use Sunburn? Let’s just find out, shall we?

I. The Code.

Let’s continue using the “Reflection/Refraction” demo to demonstrate how to use Sunburn. Please download the demo project from SG’s download section before reading on.

When you open the project with Visual Studio 2008, you will find a usual initial structure: the game class and the content folder.

In what follows, I’ll be explaining what’s included in those, based on the example, but only for the code relative to the engine itself:

i. Using Statements

There’s plenty of namespaces to refer, but for this example I will only concentrate my comments on two of them:

   1: using SynapseGaming.LightingSystem.Effects.Forward;
   2: ...
   3: using SynapseGaming.LightingSystem.Rendering.Forward;

In case you decide to use Deferred Rendering, you’ll have to change “Forward” for “Deferred” here (and then, as you will notice based on the syntax checking done by VS08, you’ll also have to modify some parts of the code, accordingly).

ii. Fields

The additional fields added to the game class, in comparison to a standard one, can be separated in three categories: a) the lighting system, b) scene members, and c) the technique members.

a) basically, you must declare here both the lighting and rendering managers, plus the helpers that will provide specific data to the system, like environmental information and lighting plus detail preferences.

   1: LightingSystemManager lightingSystemManager;
   2: RenderManager renderManager;
   3: ...
   4: SceneState sceneState;
   5: SceneEnvironment environment;
   6: LightingSystemPreferences preferences;
   7: ...
   8: DetailPreference renderQuality = DetailPreference.Off;

b) along with the meshes that conforms the scenegraph, you must declare all the lights that will be used in the scene plus its respective “rigs”; now, what’s a rig? It’s a container that will store, organize and help sharing the scene lights in the scene.

   1: ...
   2: LightRig lightRig;
   3: PointLight keyLight;
   4: PointLight fillLight;
   5: DirectionalLight sunLight;
   6: ...

c) mainly, you must declare the forward-rendering effects along with the render target helpers (the latter give support for reflection, refraction and the usual render-to-texture draw calls).

   1: SasEffect orbEffect;
   2: SasEffect waterEffect;
   3: ...
   4: RenderTargetHelper refractionTarget;
   5: RenderTargetHelper waterReflectionTarget;

iii. Constructor

Moving onto the initialization members, you must instantiate most of the above-mentioned fields and set the lighting and detail preferences based on the target platform.

   1: // Load the user preferences (example - not required).
   2: preferences = new LightingSystemPreferences();
   3: #if !XBOX
   4: if (File.Exists(userPreferencesFile))
   5:     preferences.LoadFromFile(userPreferencesFile);
   6: else
   7: #endif
   8: {
   9:     preferences.EffectDetail = DetailPreference.High;
  10:     preferences.MaxAnisotropy = 1;
  11:     preferences.PostProcessingDetail = DetailPreference.High;
  12:     preferences.ShadowDetail = DetailPreference.Low;
  13:     preferences.ShadowQuality = 1.0f;
  14:     preferences.TextureQuality = DetailPreference.High;
  15:     preferences.TextureSampling = SamplingPreference.Anisotropic;
  16: }

It is interesting to notice, in case of the PC platform, that the example provides means of selecting the level of detail based on the manufacturer of the gfx card and model number.

   1: // Pick the best performance options based on hardware.
   2:  VideoHardwareHelper hardware = new VideoHardwareHelper();
   3:  
   4:  if (hardware.Manufacturer == VideoHardwareHelper.VideoManufacturer.Nvidia)
   5:  {
   6:      if (hardware.ModelNumber >= 8800  hardware.ModelNumber < 1000)
   7:          renderQuality = DetailPreference.High;
   8:      else if (hardware.ModelNumber >= 7800)
   9:          renderQuality = DetailPreference.Medium;
  10:      else if (hardware.ModelNumber >= 6800)
  11:          renderQuality = DetailPreference.Low;
  12:  }
  13:  else if (hardware.Manufacturer == VideoHardwareHelper.VideoManufacturer.Ati)
  14:  {
  15:      if (hardware.ModelNumber >= 3800)
  16:          renderQuality = DetailPreference.High;
  17:      else if (hardware.ModelNumber >= 3400)
  18:          renderQuality = DetailPreference.Medium;
  19:      else if (hardware.ModelNumber >= 2600)
  20:          renderQuality = DetailPreference.Low;
  21:  }
  22:  
  23:  switch (renderQuality)
  24:  {
  25:      case DetailPreference.High:
  26:          reflectionRefractionTargetSize = 512;
  27:          reflectionRefractionTargetMultiSampleType = MultiSampleType.TwoSamples;
  28:          graphics.PreferMultiSampling = true;
  29:          break;
  30:      case DetailPreference.Medium:
  31:          reflectionRefractionTargetSize = 256;
  32:          reflectionRefractionTargetMultiSampleType = MultiSampleType.TwoSamples;
  33:          graphics.PreferMultiSampling = true;
  34:          break;
  35:      case DetailPreference.Low:
  36:          reflectionRefractionTargetSize = 128;
  37:          reflectionRefractionTargetMultiSampleType = MultiSampleType.TwoSamples;
  38:          graphics.PreferMultiSampling = false;
  39:          break;
  40:      case DetailPreference.Off:
  41:          reflectionRefractionTargetSize = 128;
  42:          reflectionRefractionTargetMultiSampleType = MultiSampleType.None;
  43:          graphics.PreferMultiSampling = false;
  44:          break;
  45:  }

Important: since the engine’s render manager uses by default a “page” size of 2048 pixels for shadow mapping, in case of targeting the Xbox 360, this value must be reduced to 1024 pixels so that the page completely fits within the 360’s EDRAM, thus avoiding predicated tiling.

   1: ...
   2: renderManager.ShadowManager.PageSize = 1024;
   3: ...

iv. Loading Content:

First, you must create the render target helpers and apply the preferences, in this case, to render the reflection and refraction effects, to the helper that corresponds.

   1: // Create reflection / refraction targets.  Note the refraction target is using
   2: // the "Standard" type to avoid clipping as the map is used by all refractive
   3: // objects (not just one with a specific surface plane).  See the comments at the
   4: // top of the page for details on why this is done.
   5:  
   6: refractionTarget = new RenderTargetHelper(graphics, RenderTargetHelper.TargetType.Standard,
   7:     reflectionRefractionTargetSize, reflectionRefractionTargetSize, 1, SurfaceFormat.Color,
   8:     reflectionRefractionTargetMultiSampleType, 0, RenderTargetUsage.PlatformContents);
   9:  
  10: waterReflectionTarget = new RenderTargetHelper(graphics, RenderTargetHelper.TargetType.Reflection,
  11:     reflectionRefractionTargetSize, reflectionRefractionTargetSize, 1, SurfaceFormat.Color,
  12:     reflectionRefractionTargetMultiSampleType, 0, RenderTargetUsage.PlatformContents);
  13:  
  14:  
  15: // Setup the refraction and reflection preferences.  These preferences are
  16: // set to a lower quality than the main scene's rendering to increase performance
  17: // and because reflection / refraction distortions from the normal map will
  18: // hide the quality.
  19:  
  20: refractionPreferences = new LightingSystemPreferences();
  21: refractionPreferences.EffectDetail = DetailPreference.Low;
  22: refractionPreferences.MaxAnisotropy = 0;
  23: refractionPreferences.PostProcessingDetail = DetailPreference.Low;
  24: refractionPreferences.ShadowDetail = DetailPreference.Low;
  25: refractionPreferences.ShadowQuality = 0.25f;
  26: refractionPreferences.TextureSampling = SamplingPreference.Trilinear;
  27:  
  28: refractionTarget.ApplyPreferences(refractionPreferences);
  29: waterReflectionTarget.ApplyPreferences(refractionPreferences);

Then you must read from disk the values that specify how to set the effects in order to use them as materials.

   1: // Load the custom materials / effects used by the additional reflection / refraction
   2: // rendering pass.  These materials both use the same FX file with different material options.
   3:  
   4: orbEffect = Content.Load<SasEffect>("Effects/Orb");
   5: waterEffect = Content.Load<SasEffect>("Effects/Water");

The content of the raw “.mat” files is as follows:

//-----------------------------------------------
// Synapse Gaming - SunBurn Lighting System
// Exported from the SunBurn material editor
//-----------------------------------------------
 
Locale: en-US
 
AffectsRenderStates: False
BlendColor: 0.6 0.6 0.4
BlendColorAmount: 0
BumpAmount: 0.017
BumpTexture: ""
DoubleSided: False
EffectFile: "ReflectionRefraction.fx"
Invariant: False
ReflectAmount: 0.5
ReflectTexture: ""
RefractTexture: ""
ShadowGenerationTechnique: ""
Technique: "Technique1"
Tint: 0.8627451 0.9254902 0.9647059
Transparency: 0.5
TransparencyMapParameterName: "ReflectTexture"
TransparencyMode: None

Next, you load the structure of the Light Rig, which declares each light and its respective settings and traverse the structure to instantiate and set each light.

   1: // LightRigs contain many lights and light groups.
   2: lightRig = Content.Load<LightRig>("Lights/Lights");
   3:  
   4: // Need to find the lights for later performance adjustments.
   5: foreach (ILightGroup group in lightRig.LightGroups)
   6: {
   7:     foreach (ILight light in group.Lights)
   8:     {
   9:         if (light is PointLight)
  10:         {
  11:             PointLight pointlight = light as PointLight;
  12:  
  13:             if (pointlight.Name == "FillLight")
  14:                 fillLight = pointlight;
  15:             else if (pointlight.Name == "KeyLight")
  16:                 keyLight = pointlight;
  17:         }
  18:         else if (light is DirectionalLight)
  19:         {
  20:             DirectionalLight dirlight = light as DirectionalLight;
  21:  
  22:             if (dirlight.Name == "Sun")
  23:                 sunLight = dirlight;
  24:         }
  25:     }
  26: }

The content of the raw “.rig” files is as follows:

<root>
  <LightRig>
    <LightGroups>
      <GroupList>
        <item_0>
          <LightGroup>
            <Name>EnvLighting</Name>
            <ShadowType>SceneLifeSpanObjects</ShadowType>
            <Position>
              <Vector3>
                <X>0</X>
                <Y>0</Y>
                <Z>0</Z>
              </Vector3>
            </Position>
            <Radius>0</Radius>
            <ShadowQuality>0.5</ShadowQuality>
            <ShadowPrimaryBias>1</ShadowPrimaryBias>
            <ShadowSecondaryBias>0.2</ShadowSecondaryBias>
            <ShadowPerSurfaceLOD>True</ShadowPerSurfaceLOD>
            <ShadowGroup>False</ShadowGroup>
            <Lights>
              <LightList>
                <item_0>
                  <AmbientLight>
                    <Name>Ambient Lighting</Name>
                    <Enabled>True</Enabled>
                    <DiffuseColor>
                      <Vector3>
                        <X>1</X>
                        <Y>0.6431373</Y>
                        <Z>0.04313726</Z>
                      </Vector3>
                    </DiffuseColor>
                    <Intensity>0.3</Intensity>
                  </AmbientLight>
                </item_0>
                <item_1>
                  <DirectionalLight>
                    <Name>Sun</Name>
                    <Enabled>True</Enabled>
                    <DiffuseColor>
                      <Vector3>
                        <X>1</X>
                        <Y>0.972549</Y>
                        <Z>0.772549</Z>
                      </Vector3>
                    </DiffuseColor>
                    <Intensity>2.6</Intensity>
                    <ShadowType>AllObjects</ShadowType>
                    <Direction>
                      <Vector3>
                        <X>-0.5012565</X>
                        <Y>-0.8552828</Y>
                        <Z>-0.1312759</Z>
                      </Vector3>
                    </Direction>
                    <ShadowQuality>2</ShadowQuality>
                    <ShadowPrimaryBias>1.3</ShadowPrimaryBias>
                    <ShadowSecondaryBias>0.01</ShadowSecondaryBias>
                    <ShadowPerSurfaceLOD>True</ShadowPerSurfaceLOD>
                  </DirectionalLight>
                </item_1>
              </LightList>
            </Lights>
          </LightGroup>
        </item_0>
        <item_1>
          <LightGroup>
            <Name>OrbLighting</Name>
            <ShadowType>SceneLifeSpanObjects</ShadowType>
            <Position>
              <Vector3>
                <X>0</X>
                <Y>0</Y>
                <Z>0</Z>
              </Vector3>
            </Position>
            <Radius>0</Radius>
            <ShadowQuality>0.5</ShadowQuality>
            <ShadowPrimaryBias>1</ShadowPrimaryBias>
            <ShadowSecondaryBias>0.2</ShadowSecondaryBias>
            <ShadowPerSurfaceLOD>True</ShadowPerSurfaceLOD>
            <ShadowGroup>False</ShadowGroup>
            <Lights>
              <LightList>
                <item_0>
                  <PointLight>
                    <Name>FillLight</Name>
                    <Enabled>True</Enabled>
                    <DiffuseColor>
                      <Vector3>
                        <X>0.3803922</X>
                        <Y>0.8313726</Y>
                        <Z>0.9411765</Z>
                      </Vector3>
                    </DiffuseColor>
                    <Intensity>3.8</Intensity>
                    <FillLight>True</FillLight>
                    <FalloffStrength>0</FalloffStrength>
                    <ShadowType>AllObjects</ShadowType>
                    <Position>
                      <Vector3>
                        <X>25.83315</X>
                        <Y>10.99056</Y>
                        <Z>-75.42744</Z>
                      </Vector3>
                    </Position>
                    <Radius>46</Radius>
                    <ShadowQuality>0</ShadowQuality>
                    <ShadowPrimaryBias>1</ShadowPrimaryBias>
                    <ShadowSecondaryBias>0.2</ShadowSecondaryBias>
                    <ShadowPerSurfaceLOD>True</ShadowPerSurfaceLOD>
                  </PointLight>
                </item_0>
                <item_1>
                  <PointLight>
                    <Name>KeyLight</Name>
                    <Enabled>True</Enabled>
                    <DiffuseColor>
                      <Vector3>
                        <X>0.4627451</X>
                        <Y>0.8980392</Y>
                        <Z>1</Z>
                      </Vector3>
                    </DiffuseColor>
                    <Intensity>0.6</Intensity>
                    <FillLight>False</FillLight>
                    <FalloffStrength>0</FalloffStrength>
                    <ShadowType>AllObjects</ShadowType>
                    <Position>
                      <Vector3>
                        <X>25.83315</X>
                        <Y>10.99056</Y>
                        <Z>-75.42744</Z>
                      </Vector3>
                    </Position>
                    <Radius>110</Radius>
                    <ShadowQuality>0.25</ShadowQuality>
                    <ShadowPrimaryBias>1</ShadowPrimaryBias>
                    <ShadowSecondaryBias>0.2</ShadowSecondaryBias>
                    <ShadowPerSurfaceLOD>True</ShadowPerSurfaceLOD>
                  </PointLight>
                </item_1>
              </LightList>
            </Lights>
          </LightGroup>
        </item_1>
      </GroupList>
    </LightGroups>
  </LightRig>
</root>

Finally, you load the data that configures the environment.

   1: // Load the scene settings.
   2: environment = Content.Load<SceneEnvironment>("Environment/Environment");

Being the raw content of the “.env” files the following:

<root>
  <SceneEnvironment>
    <VisibleDistance>500</VisibleDistance>
    <FogEnabled>True</FogEnabled>
    <FogColor>
      <Vector3>
        <X>0</X>
        <Y>0</Y>
        <Z>0</Z>
      </Vector3>
    </FogColor>
    <FogStartDistance>70</FogStartDistance>
    <FogEndDistance>200</FogEndDistance>
    <ShadowFadeStartDistance>500</ShadowFadeStartDistance>
    <ShadowFadeEndDistance>5000</ShadowFadeEndDistance>
    <ShadowCasterDistance>5000</ShadowCasterDistance>
    <BloomAmount>2</BloomAmount>
    <BloomThreshold>0.7</BloomThreshold>
    <ExposureAmount>1</ExposureAmount>
    <DynamicRangeTransitionMaxScale>4.5</DynamicRangeTransitionMaxScale>
    <DynamicRangeTransitionMinScale>0.5</DynamicRangeTransitionMinScale>
    <DynamicRangeTransitionTime>0.5</DynamicRangeTransitionTime>
  </SceneEnvironment>
</root>

The remaining tasks are the usual ones with the exception of what relates to the local field of the type “EffectBatchHelper”.

   1: ...
   2: EffectBatchHelper batcher = new EffectBatchHelper();
   3: batcher.CollapseEffects(scene);
   4: ...

What’s its use? It helps to create batches of effects, by analyzing the effects used by each of the models in the scene. Or in other words, it collapses similar “materials” in order to optimize draw calls.

v. Updating:

The only special thing to notice here has to do with the water effect, since on every update call it calculates the normal texture to set to achieve the animation effect on the water’s surface.

   1: // Apply the current water animation "frame" to the water effects.
   2: for (int p = 0; p < water.MeshParts.Count; p++)
   3: {
   4:     ModelMeshPart part = water.MeshParts[p];
   5:     if (part.Effect is LightingEffect)
   6:         (part.Effect as LightingEffect).NormalMapTexture = waternormalmapframe;
   7: }

Other than that, there’s nothing else to comment since all handlers will be automatically updated by the game instance it-self.

vi. Drawing:

As said on the beginning of this article, the game renders the refraction texture, then the reflection one and finally the main output.

I order to achieve this goal, you first must set up the state of the scene.

   1: // Setup the scene state.
   2: sceneState.BeginFrameRendering(view, projection, gameTime, environment);
   3: ...

Then, for each “special” map (in this case, in order, the refraction and reflection ones), select the render target, what lights are active and what objects they affect, and draw the scene.

   1: //-------------------------------------------
   2: // Generate the refraction map.
   3:  
   4: // Adjust the reflection / refraction lighting based on performance.
   5: if (renderQuality == DetailPreference.High)
   6: {
   7:     keyLight.Enabled = true;
   8:     keyLight.ShadowType = ShadowType.AllObjects;
   9:     fillLight.Enabled = true;
  10:     fillLight.ShadowType = ShadowType.AllObjects;
  11:     sunLight.Enabled = true;
  12: }
  13: else
  14: {
  15:     keyLight.Enabled = false;
  16:     keyLight.ShadowType = ShadowType.None;
  17:     fillLight.Enabled = true;
  18:     fillLight.ShadowType = ShadowType.None;
  19:     sunLight.Enabled = true;
  20: }
  21:  
  22: // Add the light rig.
  23: renderManager.LightManager.SubmitLightRig(lightRig, ObjectLifeSpan.Frame);
  24:  
  25: // Begin generating the refraction map.
  26: refractionTarget.BeginFrameRendering(sceneState);
  27:  
  28: // Clear the depth buffer then render.
  29: graphics.GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Gray, 1.0f, 0);
  30: RenderTarget(refractionTarget);
  31: refractionTarget.EndFrameRendering();

   1: //-------------------------------------------
   2: // Generate the water reflection map.
   3:  
   4: // Adjust the reflection / refraction lighting based on performance.
   5: if (renderQuality != DetailPreference.High)
   6:     sunLight.Enabled = false;
   7:  
   8: // Add the light rig.
   9: renderManager.LightManager.SubmitLightRig(lightRig, ObjectLifeSpan.Frame);
  10:  
  11: // The water reflection map includes the orbs so add them as dynamic frame objects.
  12: foreach (Orb orb in orbs)
  13:     renderManager.SubmitRenderableObject(orb.model, orb.mesh, orb.currentMeshToObject, sceneWorld, false, ObjectLifeSpan.Frame);
  14:  
  15: // Begin generating the water reflection map.
  16: waterReflectionTarget.BeginFrameRendering(sceneState, waterWorldPlane);
  17:  
  18: // Clear the depth buffer then render.
  19: graphics.GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Gray, 1.0f, 0);
  20: RenderTarget(waterReflectionTarget);
  21: waterReflectionTarget.EndFrameRendering();

Lastly, the scene is rendered for the third time combining both previous maps and thus, getting the final output for the scene.

   1: //-------------------------------------------
   2: // Render the main scene.
   3:  
   4: // Adjust the lighting based on performance.
   5: if (renderQuality == DetailPreference.High  renderQuality == DetailPreference.Medium)
   6: {
   7:     keyLight.Enabled = true;
   8:     keyLight.ShadowType = ShadowType.AllObjects;
   9:     fillLight.Enabled = true;
  10:     fillLight.ShadowType = ShadowType.AllObjects;
  11:     sunLight.Enabled = true;
  12: }
  13: else
  14: {
  15:     keyLight.Enabled = renderQuality != DetailPreference.Off;
  16:     keyLight.ShadowType = ShadowType.AllObjects;
  17:     fillLight.Enabled = true;
  18:     fillLight.ShadowType = ShadowType.None;
  19:     sunLight.Enabled = true;
  20: }
  21:  
  22: // Add the light rig.
  23: renderManager.LightManager.SubmitLightRig(lightRig, ObjectLifeSpan.Frame);
  24:  
  25: // The main rendering pass includes all objects so add the water and orbs as dynamic frame objects.
  26: foreach (Orb orb in orbs)
  27:     renderManager.SubmitRenderableObject(orb.model, orb.mesh, orb.currentMeshToObject, sceneWorld, false, ObjectLifeSpan.Frame);
  28: renderManager.SubmitRenderableObject(scene, water, waterMeshToObject, sceneWorld, false, ObjectLifeSpan.Frame);
  29:  
  30:  
  31: // Apply main scene preferences (higher quality than reflection / refraction).
  32: renderManager.ApplyPreferences(preferences);
  33:  
  34: // Begin main frame rendering.
  35: editor.BeginFrameRendering(sceneState);
  36: renderManager.BeginFrameRendering(sceneState);
  37:  
  38: // Clear the depth buffer then render.
  39: graphics.GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
  40: renderManager.Render();
  41:  
  42:  
  43: // Setup and render reflective / refractive pass for water and orbs using additive blending.
  44: GraphicsDevice.RenderState.AlphaBlendEnable = true;
  45: GraphicsDevice.RenderState.SourceBlend = Blend.One;
  46: GraphicsDevice.RenderState.DestinationBlend = Blend.One;
  47:  
  48: foreach (Orb orb in orbs)
  49:     RenderMesh(orb.mesh, orb.currentMeshToObject * sceneWorld, sceneState, orbEffect, null, refractionTarget.GetTexture());
  50:  
  51: GraphicsDevice.RenderState.CullMode = CullMode.None;
  52:  
  53: RenderMesh(water, waterMeshToObject * sceneWorld, sceneState, waterEffect,
  54:     waterReflectionTarget.GetTexture(), refractionTarget.GetTexture());
  55:  
  56: GraphicsDevice.RenderState.AlphaBlendEnable = false;
  57: GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
  58:  
  59:  
  60: // Done rendering main frame.
  61: renderManager.EndFrameRendering();
  62: editor.EndFrameRendering();

Each rendering step is started and ended in a similar way than the “SpriteBatch” class does, so there are no surprises here.

Finally, we’re done rendering the scene frame.

   1: sceneState.EndFrameRendering();
   2: ...

vii. Overall:

All of the provided demo projects, include a detail explanation on what is going on in each example, what greatly help you understand the rationale behind the engine’s processes.

II. The Editor.

Now, you may be wondering: “how can I speed up the modeling process? If I had to manually write material files and so on, I’d be cumbersome!”.

You’re right, but, fortunately the engine comes with a handy editor that spares you from that task.

How? Simple, by using the game class provided by Synapse Gaming, you can change materials, and position lights, among other things.

Note: in order to use the editor you must add one more field to your code: the LightingSystemEditor field.

The following video says it all:

For more videos showing off the Sunburn engine please visit this site:

http://www.youtube.com/user/bobthecbuilder

Well, this is it. Now it’s your turn to do your own tests and share your experience with the Sunburn engine!

Watch this space,
~Pete

 

> Link to Spanish version.