Home > TutorialsComputer Graphics Essentials
2.4. Tiling a texture in a plane
In this tutorial you will learn how to repeat the same texture over and over again to create the effect of increasing the texture resolution.



Click here to go to the forum discussion of this tutorial


 

2.4.1. Prerequisites
default.jpg
Before you start this tutorial make sure that you have:
  • Installed XNA Game Studio 3.1
  • Downloaded the starter project from the downloads section
  • Successfully run the starter project
  • For a better understanding, is preferred to have finished 2.3.
   
 

2.4.2. Game Assets 
Game assets [2.3.2] for this tutorial:

 

2.4.3. The using statements
Besides the using statements from the starter project, this tutorial needs the following using statements:

using BetaCell.Environment.Mesh;
using BetaCell.Behavior.Procedural;
using BetaCell.Environment.Texture.Feeder;
using BetaCell.Environment.Mesh.Visitors;
using BetaCell.Environment;
using BetaCell.Environment.Light;
using BetaCell.Util.Procedural;
using BetaCell.Common.Vertex;
using BetaCell.Environment.Texture;
using BetaCell.Effects;
using BetaCell.Effects.Composer
;

 

2.4.4. Game Attributes 
The attributes that come with the starter project are explained in tutorial section 1.1.4.

For this tutorial we are going to be needing the following attributes:

BCMesh plane;
BCProceduralRotator rotator;

 

Advertisement
 

2.4.5. The Initialize method 
Most of the concepts of this initialization method where covered in [2.3.5]; this tutorial focuss in the texture scale attribute of the texture feeder.

default.jpg
Look at the figure and compare it to the one in section 2.4.1, the main difference is the size of the hexagons on the ground.

In both figures, the ground is a plane of the same size, the same texture is applied and the same effect is set. The difference is the scale of the texture (1 in this figure, 5 in the one of 2.4.1).

The following lines create the texture strategy and set the scale.

BCTextureStrategy memory =
    new BCMemoryTextureStrategy(roadTexture);
memory.setScale(new Vector2(5, 5));



When you create a plane [1.2.4] with a texture scale of 1 (last parameter of the constructor), the texture coordinates for all the points in the plane are in the 0 to 1 range.

default.jpg
When the basic texture effect is being applied; the texture coordinates for all the points in the plane get multiplied by the texScale attribute of the associated texture feeder; if the texture coordinates of the plane are in the range 0 to 1, then the resultant texture coordinates end up outside the range 0 to 1 like the ones in the figure.

Don't get confused by involving the 3D position of the points, this example focuses in texture coordinates only.

So the question now is what happens if the UV coordinates of a face are outside the 0 to 1 range. 

default.jpg
The answer to the question above depends on the address mode of the sampler.

new BCSampler(
    "LINEAR", "LINEAR", "LINEAR", "4",
    "WRAP", "WRAP", "0xffffffff"
)

We can define different address modes for U and V, in the code above the fifth and the sixth parameters are the address mode for U and V respectively.

  

In the figure for both U and V, the upper left part is an address mode "WRAP" (repeats), the upper right part is "MIRROR", the lower left part is "CLAMP" and the lower right part is "BORDER". For the border address mode the, the color of the border is the seventh parameter of the sampler.

default.jpg
In this example we are using the "WRAP" address mode, the texture of this tutorial is "tillable" which means that the left border coincides with the right border and the top border with the bottom border.

You can test with different texScale values to see the effect it has on how the texture is applied.

 

 

2.4.6. The Draw method
Nothing new, just draw the plane:

plane.Draw(gameTime);

 

2.4.7. Conclusion
This tutorial is almost the same as 2.3, it's purpose is to explain what the address mode. This technique is very useful when you have a large terrain.

Click here to go to the forum discussion of this tutorial

 

Advertisement
 

2.4.8. Complete source code
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using BetaCell.Debug;
using BetaCell.Util.GlobalInfo.Content;
using BetaCell.Environment.Camera;
using BetaCell.Util.GlobalInfo;

using BetaCell.Environment.Mesh;
using BetaCell.Behavior.Procedural;
using BetaCell.Environment.Texture.Feeder;
using BetaCell.Environment.Mesh.Visitors;
using BetaCell.Environment;
using BetaCell.Environment.Light;
using BetaCell.Util.Procedural;
using BetaCell.Common.Vertex;
using BetaCell.Environment.Texture;
using BetaCell.Effects;
using BetaCell.Effects.Composer;

namespace Starter
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //Game attributes
        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        ContentManager content;

        BCFirstPersonHumanCamera camera;
        //end game attributes

        //This tutorial's attributes
        BCMesh plane;
        BCProceduralRotator rotator;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;
            content = new ContentManager(Services);
#if(XBOX360)
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
#else
            graphics.PreferredBackBufferWidth = 852;
            graphics.PreferredBackBufferHeight = 480;
#endif
        }

        protected override void Initialize()
        {
            //base initialization
            device = graphics.GraphicsDevice;

            BCLogger.instance.init(device, content);
            BCInitializationManager.initialize(content);

            OnActivated(null, null);

            recreateScene();
            rotator = new BCProceduralRotator(0, 0.0f, 0.1f, 0f, 1000.0f);

            base.Initialize();
        }

        private void recreateScene()
        {
            //Creating a base material for the cube
            BCMaterialBase material = new BCMaterialBase();
            material.specRange = 0.8f;
            material.diff = Color.White;
            material.spec = Color.White;
            material.amb = Color.LightGray;
            BCMaterialVisitor materialVisitor = new BCMaterialVisitor(material);

            //Color set effect composition part
            BCColorSet colorSet = new BCColorSet(Color.Black);
            BCFunction baseColor = colorSet.getFunction("Shader.ColorSet");
            BCDynamicEffectVisitor colorSetter = new BCDynamicEffectVisitor(baseColor, colorSet, 0);

            //Light effect composition part
            BCBasicLight light = new BCBasicLight();
            light.range = 200;
            light.transform = Matrix.CreateTranslation(20, 20, 10);
            light.constAtten = 1;
            light.linAtten = 0;
            light.cubAtten = 0;
            light.spotRange = 0.9f;
            light.useShadows = false;

            BCFunction lightPart = light.getFunction("Shader.Light.Classic.VertexPointLight");
            BCDynamicEffectVisitor lightSetter = new BCDynamicEffectVisitor(lightPart, light, 1);

            //Creating a sampler for the texture
            BCSampler sampler = new BCSampler(
                "LINEAR", "LINEAR", "LINEAR", "4",
                "WRAP", "WRAP", "0xffffffff"
            );

            //Creating a texture feeder for the texture
            BCTextureFeeder texFeeder = new BCTextureFeeder();
            texFeeder.addSampler("tex", sampler);

            //Getting the texture effect part function from the texture feeder
            BCFunction texturePart = texFeeder.getFunction("Shader.Texture.Texture");

            //Creating the texture effect setter visitor
            BCDynamicEffectVisitor textureEffectSetter =
                new BCDynamicEffectVisitor(texturePart, texFeeder, BCDynamicEffect.MAX_FUNCTIONS);

            //-------------
            //plane
            //-------------

            plane = ProceduralModelers.PLANE_MODELER.createPlane(4, 4, 25, 25, new Vector3(0, 0, 0),
                new BCVertexPosNorTexContainer(), 1);
            BCMeshGraphicUtil.init(plane, device);

            //Loading the texture
            Texture2D roadTexture = content.Load<Texture2D>("Content/gameAssets/road");

            //Creating a texture strategy from the texture
            BCTextureStrategy memory = new BCMemoryTextureStrategy(roadTexture);
            memory.setScale(new Vector2(5, 5));

            //Setting texture to the mesh
            BCTextureVisitor roadTex = new BCTextureVisitor("tex", memory);

            plane.visit(roadTex);
            plane.visit(materialVisitor);
            plane.visit(colorSetter);
            plane.visit(lightSetter);
            plane.visit(textureEffectSetter);

        }

        protected override void OnActivated(object sender, EventArgs args)
        {
            buildViewMatrix();
            base.OnActivated(sender, args);
        }

        void buildViewMatrix()
        {
            Vector3 pos = new Vector3(30, 10, 30);
            Vector3 look = new Vector3(-3, -1, -3);
            look.Normalize();

            camera = new BCFirstPersonHumanCamera(look, pos,
                MathHelper.PiOver4,
                (float)this.Window.ClientBounds.Width / (float)this.Window.ClientBounds.Height,
                1f, 200);

            BCGlobalInfo.instance.setMatrix(
                BCGlobalInfo.VIEW_INDEX,
                camera.getViewMatrix(-1)
            );

            BCGlobalInfo.instance.setMatrix(
                BCGlobalInfo.PROJECTION_INDEX,
                camera.getProjectionMatrix(-1)
            );
        }

        protected override void Update(GameTime gameTime)
        {
        }

        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);

            Matrix rotMatrix = rotator.getRotationMatrix(gameTime);

            plane.transform = rotMatrix;
            plane.Draw(gameTime);

            BCLogger.instance.printFPS(gameTime);
            BCLogger.instance.flush();
            base.Draw(gameTime);
        }
    }
}