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.