/**************************************************************************\
*
* This file is part of the Coin 3D visualization library.
* Copyright (C) 1998-2007 by Systems in Motion. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* ("GPL") version 2 as published by the Free Software Foundation.
* See the file LICENSE.GPL at the root directory of this source
* distribution for additional information about the GNU GPL.
*
* For using Coin with software that can not be combined with the GNU
* GPL, and for taking advantage of the additional benefits of our
* support services, please contact Systems in Motion about acquiring
* a Coin Professional Edition License.
*
* See http://www.coin3d.org/ for more information.
*
* Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
* http://www.sim.no/ sales@sim.no coin-support@coin3d.org
*
\**************************************************************************/
/*!
\class SoTextureCombine SoTextureCombine.h Inventor/nodes/SoTextureCombine.h
\brief The SoTextureCombine class is a node for setting texture combine functions.
\ingroup nodes
This node is more or less an exact mapping of the OpenGL
ARB_texture_env_combine extension (included in OpenGL in OpenGL
v1.3). See
http://oss.sgi.com/projects/ogl-sample/registry/ARB/texture_env_combine.txt
for more information about this extension.
Basically this node lets you specify up to three sources, and a
function to combine those sources. In addition to the function, it's
possible to apply simple operands on the sources. In the
documentation below, the sources will be called Arg0, Arg1, and
Arg2, just like in the ARB_texture_env_mode specification.
It's possible to specify both a color and an alpha operation.
This node has many fields, but usually it is sufficient to set only
one or very few fields. The selected operation decides which
values you need to set. One common example is to add a light map
to textured geometry. A lightmap can look like this:
The example below just shows how to apply the lightmap to a cube,
with one light source on each side of the cube. Usually the texture
coordinates are calculated so that a spot light or a point light is
simulated.
\verbatim
Texture2 { filename "wood.jpg" }
Switch {
whichChild -3 # use to toggle lightmap on/off
TextureUnit {
unit 1
}
TextureCombine {
rgbOperation ADD_SIGNED
rgbSource [PREVIOUS, TEXTURE]
rgbOperand [SRC_COLOR, SRC_COLOR ]
alphaOperation REPLACE
alphaSource [TEXTURE]
alphaOperand [SRC_ALPHA]
}
Texture2 { filename "lightmap.jpg" }
TextureUnit { unit 0 }
}
Cube { }
\endverbatim
The scene above in a viewer:
In addition to the functions you can set in rgbOperation (or
alphaOperation), it's possible to create more complex texture
functions by combining two textures that have already been
combined. You can use the SoSceneTexture2 node to create those
textures. Below is an example that shows how to implement Arg0*Arg1
+ Arg2*Arg0, where Arg0 = texture1 RGB, Arg1 = texture2 RGB, Arg2 =
texture 2 alpha:
\verbatim
ShapeHints { vertexOrdering COUNTERCLOCKWISE shapeType SOLID }
Separator {
SceneTexture2 {
size 256 256
transparencyFunction NONE
scene Separator {
OrthographicCamera {
height 2
aspectRatio 1
position 0 0 1
viewportMapping LEAVE_ALONE
}
LightModel { model BASE_COLOR }
Coordinate3 {
point [ -1 -1 0, 1 -1 0, 1 1 0, -1 1 0 ]
}
DEF texture1 Texture2 { filename "texture1.png" }
TextureUnit { unit 1 }
TextureCombine {
rgbOperation MODULATE
rgbSource [ PREVIOUS, TEXTURE ]
rgbOperand [ SRC_COLOR, SRC_COLOR ]
alphaOperation REPLACE
alphaSource [TEXTURE]
alphaOperand [ SRC_ALPHA ]
}
DEF texture2 Texture2 { filename "texture2_with_alpha.png" }
TextureCoordinate2 {
point [0 0, 1 0, 1 1, 0 1]
}
FaceSet { numVertices 4 }
}
}
TextureUnit { unit 1 }
TextureCombine {
rgbOperation ADD
rgbSource [ PREVIOUS, TEXTURE ]
rgbOperand [ SRC_COLOR, SRC_COLOR ]
alphaOperation REPLACE
alphaSource [TEXTURE]
alphaOperand [ SRC_ALPHA ]
}
SceneTexture2 {
size 256 256
transparencyFunction NONE
scene Separator {
OrthographicCamera {
height 2
aspectRatio 1
position 0 0 1
viewportMapping LEAVE_ALONE
}
LightModel { model BASE_COLOR }
Coordinate3 {
point [ -1 -1 0, 1 -1 0, 1 1 0, -1 1 0 ]
}
USE texture1
TextureUnit { unit 1 }
TextureCombine {
rgbOperation MODULATE
rgbSource [ PREVIOUS, TEXTURE ]
rgbOperand [ SRC_COLOR, SRC_ALPHA ]
alphaOperation REPLACE
alphaSource [TEXTURE]
alphaOperand [ SRC_ALPHA ]
}
USE texture2
TextureCoordinate2 {
point [0 0, 1 0, 1 1, 0 1]
}
FaceSet { numVertices 4 }
}
}
# map resulting texture onto a Cube
Cube { }
}
\endverbatim
It should be possible to create almost any kind of texture function
using this scheme, at the cost of extra texture memory usage (the
intermediate textures), of course.
FILE FORMAT/DEFAULTS:
\code
TextureCombine {
rgbSource [ ]
alphaSource [ ]
rgbOperand [ ]
alphaOperand [ ]
rgbOperation MODULATE
alphaOperation MODULATE
rgbScale 1
alphaScale 1
constantColor 1 1 1 1
}
\endcode
\since Coin 2.3
*/
// *************************************************************************
/*!
\enum SoTextureCombine::Source
For enumerating combiner sources.
*/
/*!
\enum SoTextureCombine::Operand
For enumerating source operands.
*/
/*!
\enum SoTextureCombine::Operation
For enumerating combiner operations/functions.
*/
/*!
\var SoTextureCombine::Source SoTextureCombine::PRIMARY_COLOR
Choose primary color as source.
*/
/*!
\var SoTextureCombine::Source SoTextureCombine::TEXTURE
Choose texture as source.
*/
/*!
\var SoTextureCombine::Source SoTextureCombine::CONSTANT
Choose the constantColor field as source.
*/
/*!
\var SoTextureCombine::Source SoTextureCombine::PREVIOUS
Choose the previous unit's texture as source.
*/
/*!
\var SoTextureCombine::Operand SoTextureCombine::SRC_COLOR
Use the source color as operand.
*/
/*!
\var SoTextureCombine::Operand SoTextureCombine::ONE_MINUS_SRC_COLOR
Use one minus source color as operand.
*/
/*!
\var SoTextureCombine::Operand SoTextureCombine::SRC_ALPHA
Use the source alpha as operand.
*/
/*!
\var SoTextureCombine::Operand SoTextureCombine::ONE_MINUS_SRC_ALPHA
Use one minus source alpha as operand.
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::REPLACE
dst = Arg0
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::MODULATE
dst = Arg0 * Arg1
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::ADD
dst = Arg0 + Arg1
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::ADD_SIGNED
dst = Arg0 + Arg1 - 0.5
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::SUBTRACT
dst = Arg0 - Arg1
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::INTERPOLATE
dst = Arg0 * (Arg2) + Arg1 * (1-Arg2)
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::DOT3_RGB
Dot product of Arg0 and Arg1
*/
/*!
\var SoTextureCombine::Operation SoTextureCombine::DOT3_RGBA
Dot product (including alpha) of Arg0 and Arg1
*/
/*!
SoTextureCombine::rgbSource
The color sources. This field is empty by default, but you can
insert up to three values [Arg0, Arg1, Arg2]. When the field
contains less than three values, the default [TEXTURE, PREVIOUS,
CONSTANT], will be used for missing values.
For texture unit 0, PREVIOUS maps to PRIMARY_COLOR,
*/
/*!
SoTextureCombine::alphaSource
The alpha sources. This field is empty by default, but you can
insert up to three values [Arg0, Arg1, Arg2]. When the field
contains less than three values, the default [TEXTURE, PREVIOUS,
CONSTANT], will be used for missing values.
For texture unit 0, PREVIOUS maps to PRIMARY_COLOR,
*/
/*!
SoTextureCombine::rgbOperand
The color operands. This field is empty by default, but you can
insert up to three values. When the field contains less than three
values, the default [SRC_COLOR, SRC_COLOR, SRC_COLOR] will be used
for missing values.
*/
/*!
SoTextureCombine::alphaOperand
The alpha operands. This field is empty by default, but you can
insert up to three values. When the field contains less than three
values, the default [SRC_ALPHA, SRC_ALPHA, SRC_ALPHA] will be used
for missing values. Please not that only SRC_ALPHA and
ONE_MINUS_SRC_ALPHA are valid operands for alpha operations.
*/
/*!
SoTextureCombine::rgbOperation
The color operation. Default value is MODULATE.
*/
/*!
SoTextureCombine::alphaOperation
The alpha operation. Default value is MODULATE.
*/
/*!
SoTextureCombine::rgbScale
Scale color result by this value. Supported values are 1, 2, and 4.
*/
/*!
SoTextureCombine::alphaScale
Scale alpha result by this value. Supported values are 1, 2, and 4.
*/
/*!
SoTextureCombine::constantColor
The constant color (when CONSTANT is used as source). Default value
is (1,1,1,1).
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// *************************************************************************
SO_NODE_SOURCE(SoTextureCombine);
/*!
Constructor.
*/
SoTextureCombine::SoTextureCombine(void)
{
SO_NODE_INTERNAL_CONSTRUCTOR(SoTextureCombine);
SO_NODE_ADD_FIELD(rgbSource, (TEXTURE));
SO_NODE_ADD_FIELD(alphaSource, (TEXTURE));
SO_NODE_ADD_FIELD(rgbOperand, (SRC_COLOR));
SO_NODE_ADD_FIELD(alphaOperand, (SRC_ALPHA));
this->rgbSource.setNum(0);
this->rgbSource.setDefault(TRUE);
this->alphaSource.setNum(0);
this->alphaSource.setDefault(TRUE);
this->rgbOperand.setNum(0);
this->rgbOperand.setDefault(TRUE);
this->alphaOperand.setNum(0);
this->alphaOperand.setDefault(TRUE);
SO_NODE_ADD_FIELD(rgbOperation, (MODULATE));
SO_NODE_ADD_FIELD(alphaOperation, (MODULATE));
SO_NODE_ADD_FIELD(rgbScale, (1.0f));
SO_NODE_ADD_FIELD(alphaScale, (1.0f));
SO_NODE_ADD_FIELD(constantColor, (1.0f, 1.0f, 1.0f, 1.0f));
SO_NODE_DEFINE_ENUM_VALUE(Source, PRIMARY_COLOR);
SO_NODE_DEFINE_ENUM_VALUE(Source, TEXTURE);
SO_NODE_DEFINE_ENUM_VALUE(Source, CONSTANT);
SO_NODE_DEFINE_ENUM_VALUE(Source, PREVIOUS);
SO_NODE_DEFINE_ENUM_VALUE(Operand, SRC_COLOR);
SO_NODE_DEFINE_ENUM_VALUE(Operand, ONE_MINUS_SRC_COLOR);
SO_NODE_DEFINE_ENUM_VALUE(Operand, SRC_ALPHA);
SO_NODE_DEFINE_ENUM_VALUE(Operand, ONE_MINUS_SRC_ALPHA);
SO_NODE_DEFINE_ENUM_VALUE(Operation, REPLACE);
SO_NODE_DEFINE_ENUM_VALUE(Operation, MODULATE);
SO_NODE_DEFINE_ENUM_VALUE(Operation, ADD);
SO_NODE_DEFINE_ENUM_VALUE(Operation, ADD_SIGNED);
SO_NODE_DEFINE_ENUM_VALUE(Operation, SUBTRACT);
SO_NODE_DEFINE_ENUM_VALUE(Operation, INTERPOLATE);
SO_NODE_DEFINE_ENUM_VALUE(Operation, DOT3_RGB);
SO_NODE_DEFINE_ENUM_VALUE(Operation, DOT3_RGBA);
SO_NODE_SET_MF_ENUM_TYPE(rgbSource, Source);
SO_NODE_SET_MF_ENUM_TYPE(alphaSource, Source);
SO_NODE_SET_MF_ENUM_TYPE(rgbOperand, Operand);
SO_NODE_SET_MF_ENUM_TYPE(alphaOperand, Operand);
SO_NODE_SET_SF_ENUM_TYPE(rgbOperation, Operation);
SO_NODE_SET_SF_ENUM_TYPE(alphaOperation, Operation);
}
/*!
Destructor.
*/
SoTextureCombine::~SoTextureCombine()
{
}
// Doc from superclass.
void
SoTextureCombine::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoTextureCombine, SO_FROM_COIN_2_2);
SO_ENABLE(SoGLRenderAction, SoTextureCombineElement);
}
// Doc from superclass.
void
SoTextureCombine::GLRender(SoGLRenderAction * action)
{
const cc_glglue * glue = cc_glglue_instance(action->getCacheContext());
SoTextureCombine::Operation rgbaop =
(SoTextureCombine::Operation) this->rgbOperation.getValue();
SoTextureCombine::Operation alphaop =
(SoTextureCombine::Operation) this->alphaOperation.getValue();
SbBool supported = cc_glglue_glversion_matches_at_least(glue, 1, 3, 0);
if (!supported) {
supported = cc_glglue_glext_supported(glue, "GL_ARB_texture_env_combine");
if (supported && (alphaop == DOT3_RGB || alphaop == DOT3_RGBA ||
rgbaop == DOT3_RGB || rgbaop == DOT3_RGBA)) {
supported =
cc_glglue_glext_supported(glue, "GL_ARB_texture_env_dot3");
}
}
if (supported) {
SoTextureCombine::doAction((SoAction*)action);
}
else {
static int didwarn = 0;
if (!didwarn) {
SoDebugError::postWarning("SoTextureCombine::GLRender",
"Your OpenGL driver does not support the "
"required extensions to do texture combine.");
didwarn = 1;
}
}
}
// Doc from superclass.
void
SoTextureCombine::doAction(SoAction * action)
{
SoState * state = action->getState();
int unit = SoTextureUnitElement::get(state);
SoTextureCombineElement::Source rgbsource[3] = {
SoTextureCombineElement::TEXTURE,
SoTextureCombineElement::PREVIOUS,
SoTextureCombineElement::CONSTANT
};
SoTextureCombineElement::Operand rgboperand[3] = {
SoTextureCombineElement::SRC_COLOR,
SoTextureCombineElement::SRC_COLOR,
SoTextureCombineElement::SRC_COLOR
};
SoTextureCombineElement::Source alphasource[3] = {
SoTextureCombineElement::TEXTURE,
SoTextureCombineElement::PREVIOUS,
SoTextureCombineElement::CONSTANT
};
SoTextureCombineElement::Operand alphaoperand[3] = {
SoTextureCombineElement::SRC_ALPHA,
SoTextureCombineElement::SRC_ALPHA,
SoTextureCombineElement::SRC_ALPHA
};
int i;
for (i = 0; i < this->rgbSource.getNum() && i < 3; i++) {
rgbsource[i] = (SoTextureCombineElement::Source) this->rgbSource.getValues(0)[i];
}
for (i = 0; i < this->alphaSource.getNum() && i < 3; i++) {
alphasource[i] = (SoTextureCombineElement::Source) this->alphaSource.getValues(0)[i];
}
for (i = 0; i < this->rgbOperand.getNum() && i < 3; i++) {
rgboperand[i] = (SoTextureCombineElement::Operand) this->rgbOperand.getValues(0)[i];
}
for (i = 0; i < this->alphaOperand.getNum() && i < 3; i++) {
alphaoperand[i] = (SoTextureCombineElement::Operand) this->alphaOperand.getValues(0)[i];
}
SbColor4f col;
SbVec4f tmp = this->constantColor.getValue();
col[0] = tmp[0];
col[1] = tmp[1];
col[2] = tmp[2];
col[3] = tmp[3];
const cc_glglue * glue =
cc_glglue_instance(SoGLCacheContextElement::get(state));
int maxunits = cc_glglue_max_texture_units(glue);
if (unit < maxunits) {
SoTextureCombineElement::set(state, this, unit,
(SoTextureCombineElement::Operation) this->rgbOperation.getValue(),
(SoTextureCombineElement::Operation) this->alphaOperation.getValue(),
rgbsource, alphasource,
rgboperand, alphaoperand,
col,
this->rgbScale.getValue(),
this->alphaScale.getValue());
}
}
// Doc from superclass.
void
SoTextureCombine::callback(SoCallbackAction * action)
{
// So far only SoGLRenderAction supports SoTextureCombineElement. We
// may never support multiple texture units for SoCallbackAction,
// but we reimplement the method just in case
inherited::callback(action);
}
// Doc from superclass.
void
SoTextureCombine::pick(SoPickAction * action)
{
// So far only SoGLRenderAction supports SoTextureCombineElement. We
// may never support multiple texture units for SoPickAction, but we
// reimplement the method just in case
inherited::pick(action);
}