FBX SDK Programmer's Guide > Meshes, Materials and Textures > Textures >

# Layered Textures

In this tutorial topic, we present the use of FbxLayeredTexture to layer four instances of FbxFileTexture. We also expand on the concept of FbxLayerElement to define a mesh's normals and UV coordinates. The code in this topic is written in Python, which applies directly to C++.

## Example: Layering Four Textures

Required Resources: from left to right: "one.jpg", "two.jpg", "three.jpg", "four.jpg". Observe that the width and height of these files are multiples of two (128x128), which is recommended for texture dimensions.

Sample Output: This screenshot was taken after running the Python program, and importing the resulting .fbx file in Autodesk MotionBuilder.

Program Summary: The Python program below creates a scene and exports it to a .fbx file. This scene contains a planar mesh textured using an instance of FbxLayeredTexture. This instance of FbxLayeredTexture is composed of four additively blended instances of FbxFileTexture. The FbxLayeredTexture is connected to the diffuse property of an instance of FbxSurfaceLambert. As illustrated by the following diagram, this instance of FbxSurfaceLambert is added to the FbxNode's list of materials.

The planar FbxMesh is set as the FbxNode's attribute. The normal values and UV coordinates for this mesh are set within its 0th FbxLayer using FbxLayerElementNormal and FbxLayerElementUV. The material is bound to the planar polygon when FbxMesh.BeginPolygon() is called with '0' as a parameter. This 0 refers to the (only) material contained in the parent FbxNode's list of materials, i.e. FbxSurfaceLambert mentioned previously.

NoteTo run this Python program successfully, ensure that the four .jpg images above are in the same folder as this Python program when it is executed.

    '''
    layeredTextures.py

        > creates a plane in the scene whose four textures are layered
          one on top of another.

        > requires texture files: one.jpg, two.jpg, three.jpg, four.jpg
    '''

    import fbx
    import FbxCommon

    vertices = [fbx.FbxVector4( -5, -5,  0 ), # 0 - vertex index.
                fbx.FbxVector4(  5, -5,  0 ), # 1
                fbx.FbxVector4(  5,  5,  0 ), # 2
                fbx.FbxVector4( -5,  5,  0 )] # 3

    normalPosZ = fbx.FbxVector4( 0, 0, 1 ) # positive-Z normal vector.

    # Define the filename and alpha transparency of each texture.
    textureFilenames = [ ( 'one.jpg',   0.2 ),
                         ( 'two.jpg',   0.5 ),
                         ( 'three.jpg', 0.4 ),
                         ( 'four.jpg',  1.0 ) ]

    saveFilename = 'layeredTextures.fbx'

    ###############################################################
    # Helper Function(s).                                         #
    ###############################################################
    def createPlane(pScene):
        ''' Create a planar polygon as a child to the root node. '''

        rootNode = pScene.GetRootNode()

        #======================================================
        # Node definition and mesh object creation.
        #======================================================
        # Create the node.
        newNode = fbx.FbxNode.Create( pScene, 'planeNode' )
        rootNode.AddChild( newNode )

        # Create the mesh node attribute.
        newMesh = fbx.FbxMesh.Create( pScene, 'planeMesh' )
        newNode.SetNodeAttribute( newMesh )

        # Create a Lambertian material and assign it to the mesh's node.
        applyLambertMaterial( pScene, newNode )

        # Create and apply the layered texture.
        applyLayeredTexture( pScene, newNode )

        # Set the node's shading mode to view textures.
        newNode.SetShadingMode( fbx.FbxNode.eTextureShading )

        #======================================================
        # Control points.
        #======================================================
        # Define the vertices of the polygon.
        global vertices
        newMesh.InitControlPoints( 4 )
        newMesh.SetControlPointAt( vertices[0], 0 )
        newMesh.SetControlPointAt( vertices[1], 1 )
        newMesh.SetControlPointAt( vertices[2], 2 )
        newMesh.SetControlPointAt( vertices[3], 3 )

        #======================================================
        # Normals.
        #======================================================
        # For normals and UV coordinates, we will be using the
        # Layer indexed at 0. If it doesn't exist, we will create it.
        layer = newMesh.GetLayer( 0 )
        if( not layer ):
            newMesh.CreateLayer()
            layer = newMesh.GetLayer( 0 )

        # Create a normal layer element.
        normalLayerElement = fbx.FbxLayerElementNormal.Create( newMesh, 'normals' )

        # We want to have one normal for each vertex (or control point),    # so we set the mapping mode to eByControlPoint
        normalLayerElement.SetMappingMode( fbx.FbxLayerElement.eByControlPoint )

        # Set the normal values for every control point.
        normalLayerElement.SetReferenceMode( fbx.FbxLayerElement.eDirect )

        global normalPosZ # positive-Z normals.
        normalLayerElement.GetDirectArray().Add( normalPosZ )
        normalLayerElement.GetDirectArray().Add( normalPosZ )
        normalLayerElement.GetDirectArray().Add( normalPosZ )
        normalLayerElement.GetDirectArray().Add( normalPosZ )

        # Assign the normal layer element to the mesh's layer 0.
        layer.SetNormals( normalLayerElement )

        #======================================================
        # Diffuse Channel UV Coordinates.
        #======================================================
        # Given that we are only dealing with one polygon (a square plane),
        # we can simplify the code and map the uv coordinates directly to the
        # four polygon control points.
        uvDiffuseLayerElement = fbx.FbxLayerElementUV.Create( newMesh, 'diffuseUV' )
        uvDiffuseLayerElement.SetMappingMode( fbx.FbxLayerElement.eByPolygonVertex )
        uvDiffuseLayerElement.SetReferenceMode( fbx.FbxLayerElement.eDirect )

        # Populate the direct array.
        uv0 = fbx.FbxVector2( 0, 0 )
        uv1 = fbx.FbxVector2( 1, 0 )
        uv2 = fbx.FbxVector2( 1, 1 )
        uv3 = fbx.FbxVector2( 0, 1 )
        uvDiffuseLayerElement.GetDirectArray().Add( uv0 )
        uvDiffuseLayerElement.GetDirectArray().Add( uv1 )
        uvDiffuseLayerElement.GetDirectArray().Add( uv2 )
        uvDiffuseLayerElement.GetDirectArray().Add( uv3 )

        # Assign the uv layer element to the mesh's layer 0.
        layer.SetUVs( uvDiffuseLayerElement, fbx.FbxLayerElement.eTextureDiffuse )

        #======================================================
        # Polygon Definition.
        #======================================================
        # Create one planar polygon.
        #    > The 0 in FbxMesh.BeginPolygon() refers to the index of the material to be
        #      applied to the polygon. Since we only have one material applied to our FbxNode,
        #      we use index 0.
        newMesh.BeginPolygon( 0 )
        for i in range( 0, 4 ):
            newMesh.AddPolygon( i )
        newMesh.EndPolygon()

        return newNode

    def applyLambertMaterial( pScene, pNode ):
        ''' Apply a Lambertian material to the node. '''

        black = fbx.FbxDouble3( 0, 0, 0 )
        white = fbx.FbxDouble3( 1, 1, 1 )

        material = fbx.FbxSurfaceLambert.Create( pScene, 'myMaterial' )
        material.ShadingModel.Set( 'Lambert' ) # could also use 'Phong' if we were using an instance of FbxSurfacePhong
        material.Emissive.Set( black )
        material.Ambient.Set( white )
        material.Diffuse.Set( white )
        material.TransparencyFactor.Set( 0 )

        pNode.AddMaterial( material )

    def applyLayeredTexture( pScene, pNode ):
        ''' Apply a layered texture on the node using the given texture filenames. '''
        global textureFilenames

        textures = []

        # Create the individual texture objects to be layered afterwards.
        for i in range( 0, len( textureFilenames ) ):

            textureFilename = textureFilenames[i][0]
            textureAlpha = textureFilenames[i][1]

            newTexture = fbx.FbxFileTexture.Create( pScene, 'myFileTexture_' +  str( i ) )
            newTexture.SetFileName( textureFilename )

            newTexture.SetTextureUse( fbx.FbxTexture.eStandard )
            newTexture.SetMappingType( fbx.FbxTexture.eUV )
            newTexture.SetMaterialUse( fbx.FbxFileTexture.eModelMaterial )
            newTexture.SetSwapUV( False )
            newTexture.SetTranslation( 0.0, 0.0 )
            newTexture.SetScale( 1.0, 1.0 )
            newTexture.SetRotation( 0.0, 0.0 )
            newTexture.Alpha.Set( textureAlpha )

            textures.append( newTexture )

        # Create the layered texture object, and layer the textures within it.
        layeredTexture = fbx.FbxLayeredTexture.Create( pScene, 'myLayeredTexture' )

        for i in range( 0, len( textures ) ):
            texture = textures[ i ]
            layeredTexture.ConnectSrcObject( texture )
            layeredTexture.SetTextureBlendMode( i, fbx.FbxLayeredTexture.eAdditive )

        # Connect the layered texture to the node's material indexed at 0.
        # The layered texture is connected on the material's diffuse channel.
        material = pNode.GetMaterial( 0 )
        material.Diffuse.ConnectSrcObject( layeredTexture )

    ###############################################################
    # Main.                                                       #
    ###############################################################
    def main():
        ''' Main Program. '''
        fbxManager = fbx.FbxManager.Create()
        fbxScene = fbx.FbxScene.Create( fbxManager, '' )

        # Create the textured planar mesh.
        newNode = createPlane( fbxScene )

        # Save the file.
        global saveFilename
        FbxCommon.SaveScene( fbxManager, fbxScene, saveFilename, pEmbedMedia=True )

        # Clean up.
        fbxManager.Destroy()
        del fbxManager, fbxScene, newNode

    if __name__ == '__main__':
        main()

#### See Also

- FbxLayerElementUV, FbxLayerElementNormal - UV and Normal definitions

- FbxLayerElement::EMappingMode - An enum that specifies how the layer element is mapped to the surface.

- FbxLayerElement::EReferenceMode - An enum that specifies how the IndexArray and DirectArray of a layer element are to be referenced.