7.3. Texture Nodes

SoTexture2( C++ | Java | .NET ) ,SoExtTexture2( C++ | Java | .NET ) ,SoTexture3( C++ | Java | .NET ) , and SoTextureCubeMap( C++ | Java | .NET )nodes define the image(s) for the texture map and specify how the texture wraps around the object and which texture model to use.

The textureQuality field of the SoComplexity( C++ | Java | .NET ) node controls the quality of filtering used to apply the texture. A value of 0.0 disables texturing completely, and a value of 1.0 specifies to use the highest quality of texturing. The default value for this field is 0.5.

wrapS (SoSFEnum)

Specifies how the image wraps in the s (horizontal) direction. Possible values are as follows:

REPEAT: repeat the image to fill the shape (default).

CLAMP: repeat the last row of pixels.

CLAMP_TO_BORDER: clamps texture coordinate to a range coinciding with the centers of the border texels of a texture map at each mipmap level.

CLAMP_TO_EDGE: clamps texture coordinate to a range coinciding with the centers of the edge texels of a texture map at each mipmap level.

MIRRORED_REPEAT: repeats the image, alternating the original and a mirrored copy of the image.

model (SoSFEnum)

Specifies the texture model to use. Possible values are as follows:

MODULATE: multiplies the shaded color times the texture color (the default).

DECAL: replaces the shaded color with the texture color.

BLEND: blends the shaded color and the specified blend color (see the blendColor field).

REPLACE: the texture color replaces the surface color.

ADD: add incoming fragment and texture source colors.

COMBINE: use the next SoTextureCombiner( C++ | Java | .NET ) node to combine different textures.

blendColor (SoSFColor)

Specifies the color to blend when using the BLEND texture model.

magFilter (SoSFEnum)

Specifies the OpenGL filtering method for magnification. Possible values are as follow:

AUTO: Open Inventor automatically selects the filtering method depending on SoComplexity::textureQuality.

NEAREST: the texel nearest to the center of the pixel is used.

LINEAR: a weighted linear average of the 2x2 array of texels that lie nearest the center of the pixel is used.

minFilter (SoSFEnum)

Specifies the OpenGL filtering method for minification. Possible values are the values for magFilter (AUTO, NEAREST, or LINEAR) or the following:

NEAREST_MIPMAP_NEAREST: chooses the nearest texel value from the nearest mipmap level.

LINEAR_MIPMAP_NEAREST: LINEAR within the nearest mipmap level.

NEAREST_MIPMAP_LINEAR: selects the nearest texels in each of the two nearest mipmap levels and then interpolates linearly between these two values.

LINEAR_MIPMAP_LINEAR: uses linear interpolation to compute the value in each of the two nearest mipmap levels and then interpolates linearly between these two values.

enableCompressedTexture (SoSFBool)

Enables storage of textures on the graphics board in compressed format.

enableBorder (SoSFBool)

Enables borders for textures. This means that the border is already in the texture.

borderColor (SoSFVec4f)

Specifies border color used for border texture filtering. This value is only used if enableBorder is FALSE.

maxAnisotropy (SoSFFloat)

Specifies, on a per-texture object basis, the maximum degree of anisotropy to account for in texture filtering.

internalFormat (SoSFEnum)

Internal format for texture storage. If not available on the graphics device, AUTO_INTERNAL_FORMAT is used. Default is AUTO_INTERNAL_FORMAT.

useAutoMipmap (SoSFBool)

Requests mipmaps be generated by the graphics hardware. If FALSE or not supported, then mipmaps are generated by Open Inventor. FALSE by default.

The COMBINE model takes RGB and Alpha from up to three sources and combines them together to give the color of a texel (see Figure 7.7, “ Texture Combine”.).


In the figure above, Source n RGB/Alpha can be:

  • Fragment color

  • Texture color fetched and filtered from texture unit I

  • Result of the previous texture combine

  • Constant color

The following table shows the SoTexture2( C++ | Java | .NET ) fields:

In addition to the fields shown above, SoTexture2( C++ | Java | .NET ) also has all of the fields inherited from SoTexture( C++ | Java | .NET ).

This node is similar to SoTexture2( C++ | Java | .NET ), but differs in the following significant ways:

  1. It has no image field. Data is sent directly to OpenGL

  2. It doesn’t load the texture image into system memory until the node is traversed. Depending on the scene graph, this may significantly reduce the use of system memory, e.g., if the scene has many LODs containing textures. On the other hand, when the scene graph is first traversed there may be a small delay while the textures are being loaded.

  3. It has additional features:

    • support for OpenGL paletted textures for reducing the use of texture memory.

    • maximum system memory quota allowed for managing the textures.

    • the ability to:

      • delegate the texture loading to a thread.

      • trigger the loading manually.

      • get the loading status.

      • use a callback at the start and finish of loading.

      • use a callback at the finish of unloading.

The following table shows the SoExtTexture2( C++ | Java | .NET ) fields:

In addition to the fields shown above, SoExtTexture2( C++ | Java | .NET ) also has all of the fields inherited from SoTexture( C++ | Java | .NET ).

Example 7.2. Asynchronous loading of a texture

One cube does not have a texture yet

Texture has been loaded

This example shows four textured cubes. The lower left one is being textured asynchronously. The scene is rotating and is not blocked by loading the texture image.


C++
// This function is called 10 times/second
static void
  rotationCallback(void *data, SoSensor*)
  {
  // Rotates the scene
  SoRotation *rot = (SoRotation*) data;
  SbRotation currentRotation = rot->rotation.getValue();
  currentRotation =
    SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), (float)(M_PI/90.0f)) * currentRotation;
  rot->rotation.setValue(currentRotation);
  }

// Called when starting the loading of texture1
void
  startTex1Load(void *userData, SoExtTexture2 *tex)
  {
  printf("Started loading the texture 1...\n");
  }

// Called when finishing the loading of texture1
void
  finishTex1Load(void *userData, SoExtTexture2 *tex)
  {
  printf("... Finished loading the texture 1.\n");
  }

int
  main(int argc, char **argv)
  {
  // Initialize Inventor and Xt
  Widget appWindow = SoXt::init(argv[0]);
  if (appWindow == NULL)
    exit(1);

  SoSeparator *root = new SoSeparator;
  root->ref();

  SoRotation *rot = new SoRotation;

  SoTimerSensor *rotatingSensor = new SoTimerSensor(rotationCallback, rot);
  rotatingSensor->setInterval(0.1f);    // scheduled once per 1/10th of second
  rotatingSensor->schedule();

  root->addChild(rot);

  // Cube 1
  SoTranslation *tr1 = new SoTranslation();
  root->addChild(tr1);
  tr1->translation.setValue(-1.25, -1.25, 0.);
  SoExtTexture2 *texture1 = new SoExtTexture2;
  texture1->loadingMode.setValue(SoExtTexture2::MANUAL);
  texture1->loadingThreadPriority.setValue(-1);
  texture1->filename = "test1.jpg";
  texture1->addOnLoadStartCB((SoExtTexture2::SoExtTexture2CB*)startTex1Load,
    texture1);
  texture1->addOnLoadFinishCB((SoExtTexture2::SoExtTexture2CB *finishTex1Load,
    texture1);
    root->addChild(texture1);
    root->addChild(new SoCube);

  // Cube 2
    SoTranslation *tr2 = new SoTranslation();
    root->addChild(tr2);
    tr2->translation.setValue(2.5, 0., 0.);
    SoExtTexture2 *texture2 = new SoExtTexture2;
    texture2->filename = "test2.jpg";
    root->addChild(texture2);
    root->addChild(new SoCube);

  // Cube 3
    SoTranslation *tr3 = new SoTranslation();
    root->addChild(tr3);
    tr3->translation.setValue(0., 2.5, 0.);
    SoExtTexture2 *texture3 = new SoExtTexture2;
    texture3->filename = "test3.jpg";
    root->addChild(texture3);
    root->addChild(new SoCube);

  // Cube 4
    SoSeparator *sep4 = new SoSeparator;
    SoTranslation *tr4 = new SoTranslation();
    root->addChild(tr4);
    tr4->translation.setValue(-2.5, 0., 0.);
    SoExtTexture2 *texture4 = new SoExtTexture2;
    texture4->filename = "test4.jpg";
    root->addChild(texture4);
    root->addChild(new SoCube);

  // Initialize the viewer
    SoXtExaminerViewer *viewer = new SoXtExaminerViewer(appWindow);
    viewer->setSceneGraph(root);
    viewer->setTitle("ExtTexture2 Viewer");
    viewer->setSize(SbVec2s(300, 300));

  // In Inventor 2.1, if the machine does not have hardware texture
  // mapping, we must override the default drawStyle to display textures.
    viewer->setDrawStyle(SoXtViewer::STILL, SoXtViewer::VIEW_AS_IS);
    viewer->show();

  // Launch the loading automatically
    texture1->loadingMode.setValue(SoExtTexture2::AUTO);
    texture1->syncMode.setValue(SoExtTexture2::ASYNCHRONOUS);
    texture1->loadTexture();

    SoXt::show(appWindow);
    SoXt::mainLoop();
    return 0;
  }
         

.NET
SoRotation rot;

// This function is called 10 times/second
void rotationCallback(SoSensor s)
{
  // Rotates the scene
  SbRotation currentRotation = rot.rotation.GetValue();
  currentRotation *= new SbRotation(
      new SbVec3f(0.0f, 0.0f, 1.0f), (float)(Math.PI/90.0f));
  rot.rotation.SetValue(currentRotation);
}

// Called when starting the loading of texture1
void startTex1Load(SoExtTexture2 tex)
{
  Console.WriteLine("Started loading the texture 1...\n");
}

// Called when finishing the loading of texture1
void finishTex1Load(SoExtTexture2 tex)
{
  Console.WriteLine("... Finished loading the texture 1.\n");
}

private void CreateSample()
{
  SoSeparator root = new SoSeparator();

  rot = new SoRotation();

  SoTimerSensor rotatingSensor = new SoTimerSensor();
  rotatingSensor.Action = new SoSensor.SensorCB(rotationCallback);
  SbTime interval = new SbTime();
  interval.SetValue(0.3);
  rotatingSensor.SetInterval(interval);
  rotatingSensor.Schedule();

  root.AddChild(rot);

  // Cube 1
  SoTranslation tr1 = new SoTranslation();
  root.AddChild(tr1);
  tr1.translation.SetValue(-1.25f, -1.25f, 0f);
  SoExtTexture2 texture1 = new SoExtTexture2();
  texture1.loadingMode.SetValue((int)SoExtTexture2.Loadings.MANUAL);
  texture1.loadingThreadPriority.SetValue(-1);
  texture1.filename.SetValue("$OIVNETHOME/src/Inventor/examples/data/Textures/test1.jpg");
  texture1.LoadStart += new SoExtTexture2.Texture2CB(startTex1Load);
  texture1.LoadFinish += new SoExtTexture2.Texture2CB(finishTex1Load);
  root.AddChild(texture1);
  root.AddChild(new SoCube());

  // Cube 2
  SoTranslation tr2 = new SoTranslation();
  root.AddChild(tr2);
  tr2.translation.SetValue(2.5f, 0f, 0f);
  SoExtTexture2 texture2 = new SoExtTexture2();
  texture2.filename.SetValue("$OIVNETHOME/src/Inventor/examples/data/Textures/test2.jpg");
  root.AddChild(texture2);
  root.AddChild(new SoCube());

  // Cube 3
  SoTranslation tr3 = new SoTranslation();
  root.AddChild(tr3);
  tr3.translation.SetValue(0f, 2.5f, 0f);
  SoExtTexture2 texture3 = new SoExtTexture2();
  texture3.filename.SetValue("$OIVNETHOME/src/Inventor/examples/data/Textures/test3.jpg");
  root.AddChild(texture3);
  root.AddChild(new SoCube());

  // Cube 4
  SoSeparator sep4 = new SoSeparator();
  SoTranslation tr4 = new SoTranslation();
  root.AddChild(tr4);
  tr4.translation.SetValue(-2.5f, 0f, 0f);
  SoExtTexture2 texture4 = new SoExtTexture2();
  texture4.filename.SetValue("$OIVNETHOME/src/Inventor/examples/data/Textures/test4.jpg");
  root.AddChild(texture4);
  root.AddChild(new SoCube());

  // Initialize the viewer
  SoWinExaminerViewer viewer = new SoWinExaminerViewer(this, "", true,
		  SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER);
  viewer.SetSceneGraph(root);
  viewer.SetTitle("ExtTexture2 Viewer");
  viewer.SetSize(new SbVec2s(350, 350));

  // In Inventor 2.1f, if the machine does not have hardware texture
  // mapping, we must override the default drawStyle to display textures.
  viewer.SetDrawStyle(SoWinViewer.DrawTypes.STILL, SoWinViewer.DrawStyles.VIEW_AS_IS);
  viewer.Show();

  // Launch the loading automatically
  texture1.loadingMode.SetValue((int)SoExtTexture2.Filters.AUTO);
  texture1.LoadTexture();
}
        

Java
            
          

The SoIndexedTexture2( C++ | Java | .NET )property node defines a color index texture map and parameters for that map. This map applies to the current texture unit (see the section called “ SoTextureUnit”) and is used to apply texture to subsequent shapes as they are rendered.

The image data is stored in an SoSFArray2D( C++ | Java | .NET ) . This array can contain different types of data: UNSIGNED_BYTE, UNSIGNED_SHORT, UNSIGNED_INT32, SIGNED_BYTE, SIGNED_SHORT, SIGNED_INT32, FLOAT.

Each data value coming from the SoSFArray2D( C++ | Java | .NET ) is used as an index into the color map defined by the current SoColorMap( C++ | Java | .NET )node.

The minValue and maxValue fields allow you to specify the mapping from the data to the color map. For example, if minValue is set to 10000 and maxValue to 38000, all values less than or equal to 1000 will be mapped to the entry 0 of the color map and all values greater than or equal to 38000 to the last entry. Figure 7.8, “ SoIndexedTexture2 mapping data range to color map” below illustrates the process of mapping and shows how it can be used to map only the used data range to the color map.


The following table shows the SoIndexedTexture2( C++ | Java | .NET ) fields:

minValue (SoSFFloat) maxValue (SoSFFloat)

Specifies the range of values which is mapped onto the color map (see SoColorMap( C++ | Java | .NET )). When minValue and maxValue are equal to 0 (the default), the entire range of the data type is mapped onto the color map, except in the case of float data. For example, for a color map of size N:

  • With unsigned byte values, [0-255] is mapped onto the color map range [0 - N-1]

  • With unsigned short values, [0-65535] is mapped onto the color map range [0 - N-1]

  • With signed short values, [-32768 - 32767] is mapped onto the color map range [0 - N-1].

  • With float data type, range [0-1] is mapped onto the color map [0 - N-1]

All values less than or equal to minValue will be mapped to the first entry of the color map. Similarly, all values greater than or equal to maxValue will be mapped to the last entry of the color map.

imageIndex (SoSFArray2D)

This field contains the in-memory representation of the indexed texture image. Each value of this array is a color index.

rescaleTexCoord (SoSFBool)

This field controls the way an image with non-power-of-two dimension is handled:.

  • If the graphics card supports GL_ARB_texture_non_power_of_two, this field will be ignored. The image is sent directly to OpenGL.

  • If rescaleTexCoord is FALSE (the default), the image is rescaled to the next lower power-of-two dimensions. The scaling is done with a box filter.

  • If rescaleTexCoord is TRUE, the image is not rescaled but is only copied into a texture with the next higher power-of-two dimension, and its texture coordinates are scaled by applying a texture transform in order to only display the actual image.

wrapT (SoSFEnum)

Specifies how the image wraps in the t (vertical) direction. Possible values are the same as for wrapS in the section called “Fields common to SoTexture nodes”.

In addition to the fields shown above, SoIndexedTexture2( C++ | Java | .NET ) also has all of the fields inherited from SoTexture( C++ | Java | .NET ).

The following example program: $OIVHOME/src/Inventor/examples/Features/IndexedTexture/IndexedTexture.cxx (screenshot shown below) illustrates the use of SoIndexedTexture2( C++ | Java | .NET ) and SoColorMap( C++ | Java | .NET ).


SoTexture3( C++ | Java | .NET ) has the following fields:

In addition to the fields shown above, SoTexture3( C++ | Java | .NET ) also has all of the fields inherited from SoTexture( C++ | Java | .NET ).

SoTextureCubeMap( C++ | Java | .NET ) has the following fields:

In addition to the fields shown above, SoTextureCubeMap( C++ | Java | .NET ) also has all of the fields inherited from SoTexture( C++ | Java | .NET ).

You can specify the quality of the texture you are going to use. This feature is exposed in the textureQuality field of the SoComplexity( C++ | Java | .NET )node. A value of 0.0 disables texturing, and a value of 1.0 specifies to use the highest quality texturing. The default value for this field is 0.5.

[Important]

SoTexture3( C++ | Java | .NET ) and SoTextureCubeMap( C++ | Java | .NET ): When loading several image files, be sure that all of them have the same width and height as well as the same number of components. If the images do not have the same number of components, the first image read will determine the number of components that will be used. Different images will then be reformatted according to that number of components. Similarly, the first image read will determine the width and height for all images. If an image has a different size, it will be reformatted (but not rescaled) to fit the common size. For SoTexture3( C++ | Java | .NET ), the depth of the texture is determined by the number of images specified, even if one or several image files cannot be read. Pixels for unread files are set to black. This ensures that texture coordinates will remain valid even if some files are not read.

[Important]

SoTexture3( C++ | Java | .NET ) and SoTextureCubeMap( C++ | Java | .NET ): The size of a 2D image or a 3D image (i.e., the set of 2D images that constitute the pixels for the 3D texture)) must be a power of 2 in all dimensions. This is an OpenGL requirement. Therefore, if any of the dimensions are not a power of 2, the texture is enlarged (it is never shrunk) to fit the power-of-2 requirement. For a 3D texture, unlike 2D textures where a resized texture is actually rescaled, the 3D image is not rescaled. Therefore, pixels that are added to the 3D images will not contain any color information from the original 3D image and are black. You should be aware of this when setting texture coordinates. If your 3D image is not correctly dimensioned, you may want to consider either applying a ratio to your coordinates or adding an SoTexture3Transform( C++ | Java | .NET )(see next section) node with the field scaleFactor set to compensate.

If you want to translate, rotate, or scale a texture, you need to insert a texture transform node into the scene graph before the shape node. The texture transform nodes are:

These nodes actually transform the texture coordinates. You can concatenate transforms by inserting two or more texture transform nodes. If you are using multiple textures, note that this transformation is applied to the texture coordinates of the current texture unit (SoTextureUnit( C++ | Java | .NET ) ). This allows you to change the size and position of the textures on objects.


The fields of the SoTexture2Transform( C++ | Java | .NET )node are as follows:

The fields of the SoTexture3Transform( C++ | Java | .NET )node are as follows:

In the following example, the default texture coordinates are, counter-clockwise from the lower left vertex: (0,0), (1,0), (1,1), and (0,1). In the left image, the actual (i.e., transformed) texture coordinates are: (0,0), (0.5,0), (0.5,0.5), and (0,0.5). In the right image, the actual texture coordinates are: (0,0), (2,0), (2,2), and (0,2).