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++.
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.
To 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()
FbxLayerElementUV
, FbxLayerElementNormal
- UV and Normal definitionsFbxLayerElement::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.