/**************************************************************************\
*
* 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 SoTextureCoordinateCube include/Inventor/nodes/SoTextureCoordinateCube.h
\brief The SoTextureCoordinateCube class autogenerates cubemapped texture coordinated for shapes.
\ingroup nodes
The cube used for reference when mapping is the boundingbox for the shape.
FILE FORMAT/DEFAULTS:
\code
TextureCoordinateCube {
}
\endcode
\since Coin 2.3
*/
// FIXME: Add a better class description (20040123 handegar)
// *************************************************************************
#include
#ifdef HAVE_CONFIG_H
#include
#endif // HAVE_CONFIG
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// *************************************************************************
typedef struct {
SbVec3f origo;
SbBox3f boundingbox;
SoNode * currentshape;
SoState * currentstate;
SbVec4f texcoordreturn;
} so_texcoordcube_data;
static void
so_texcoordcube_construct_data(void * closure)
{
so_texcoordcube_data * data = (so_texcoordcube_data *) closure;
data->currentshape = NULL;
data->currentstate = NULL;
data->origo = SbVec3f(0,0,0);
}
static void
so_texcoordcube_destruct_data(void * closure)
{
}
// *************************************************************************
SO_NODE_SOURCE(SoTextureCoordinateCube);
// *************************************************************************
class SoTextureCoordinateCubeP {
public:
SoTextureCoordinateCubeP(SoTextureCoordinateCube * texturenode)
: master(texturenode) { }
SbVec4f calculateTextureCoordinate(SbVec3f point, SbVec3f n);
so_texcoordcube_data * so_texcoord_get_data() {
so_texcoordcube_data * data = NULL;
data = (so_texcoordcube_data *) this->so_texcoord_storage->get();
assert(data && "Error retrieving thread data.");
return data;
}
SbStorage * so_texcoord_storage;
private:
SoTextureCoordinateCube * master;
};
static const SbVec4f & textureCoordinateCubeCallback(void * userdata, const SbVec3f & point, const SbVec3f & normal);
#undef PRIVATE
#undef PUBLIC
#define PRIVATE(p) (p->pimpl)
#define PUBLIC(p) (p->master)
// *************************************************************************
/*!
Constructor.
*/
SoTextureCoordinateCube::SoTextureCoordinateCube(void)
{
PRIVATE(this) = new SoTextureCoordinateCubeP(this);
SO_NODE_INTERNAL_CONSTRUCTOR(SoTextureCoordinateCube);
pimpl->so_texcoord_storage = new SbStorage(sizeof(so_texcoordcube_data),
so_texcoordcube_construct_data,
so_texcoordcube_destruct_data);
}
/*!
Destructor.
*/
SoTextureCoordinateCube::~SoTextureCoordinateCube()
{
delete pimpl->so_texcoord_storage;
delete PRIVATE(this);
}
// Documented in superclass.
void
SoTextureCoordinateCube::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoTextureCoordinateCube, SO_FROM_COIN_2_3);
SO_ENABLE(SoGLRenderAction, SoGLTextureCoordinateElement);
SO_ENABLE(SoCallbackAction, SoTextureCoordinateElement);
SO_ENABLE(SoPickAction, SoTextureCoordinateElement);
}
const SbVec4f &
textureCoordinateCubeCallback(void * userdata,
const SbVec3f & point,
const SbVec3f & normal)
{
SoTextureCoordinateCubeP * pimpl = (SoTextureCoordinateCubeP *) userdata;
so_texcoordcube_data * data = pimpl->so_texcoord_get_data();
SoState * state = data->currentstate;
SoFullPath * path = (SoFullPath *) state->getAction()->getCurPath();
SoNode * node = path->getTail();
if (!node->isOfType(SoShape::getClassTypeId())) {
// FIXME: A better way to handle this? (20040122 handegar)
assert(FALSE && "TextureCoordinateCube callback called for a non-SoShape node.");
}
// Cast the node into a shape
SoShape * shape = (SoShape *) node;
if (shape != data->currentshape) {
data->boundingbox.makeEmpty();
const SoBoundingBoxCache * bboxcache = shape->getBoundingBoxCache();
if (bboxcache && bboxcache->isValid(state)) {
data->boundingbox = bboxcache->getProjectedBox();
data->origo = data->boundingbox.getCenter();
}
else {
shape->computeBBox(state->getAction(), data->boundingbox, data->origo);
data->origo = data->boundingbox.getCenter();
}
data->currentshape = shape;
// Expanding the bbox making it cube shaped
float sx, sy, sz;
data->boundingbox.getSize(sx, sy, sz);
if (sy > sx) sx = sy;
if (sz > sx) sx = sz;
sx *= 0.5f;
const SbVec3f c = data->origo;
data->boundingbox.setBounds(c[0] - sx, c[1] - sx, c[2] - sx,
c[0] + sx, c[1] + sx, c[2] + sx);
}
const SbVec4f & ret = pimpl->calculateTextureCoordinate(point, normal);
data->texcoordreturn = ret;
return data->texcoordreturn;
}
SbVec4f
SoTextureCoordinateCubeP::calculateTextureCoordinate(SbVec3f point, SbVec3f n)
{
so_texcoordcube_data * data = this->so_texcoord_get_data();
double maxv = fabs(n[0]);
int maxi = 0;
if (fabs(n[1]) > maxv) { maxi = 1; maxv = fabs(n[1]); }
if (fabs(n[2]) > maxv) { maxi = 2; }
int i0 = (maxi + 1) % 3;
int i1 = (maxi + 2) % 3;
const SbVec3f bmax = data->boundingbox.getMax();
const SbVec3f bmin = data->boundingbox.getMin();
float d0 = bmax[i0] - bmin[i0];
float d1 = bmax[i1] - bmin[i1];
if (d0 == 0.0f) d0 = 1.0f;
if (d1 == 0.0f) d1 = 1.0f;
float s = (point[i0] - bmin[i0]) / d0;
float t = (point[i1] - bmin[i1]) / d1;
SbVec4f tc(s, t, 0.0f, 1.0f);
switch (maxi) { // Flip textures according to projected cube-side
case 0:
tc[0] = 1.0f - t;
tc[1] = s;
break;
case 1:
tc[0] = t;
tc[1] = 1.0f - s;
break;
}
if (n[maxi] < 0.0f) {
if (maxi == 1) {
tc[1] = 1.0f - tc[1];
}
else {
tc[0] = 1.0f - tc[0];
}
}
return tc;
}
// Documented in superclass.
void
SoTextureCoordinateCube::doAction(SoAction * action)
{
}
// Documented in superclass.
void
SoTextureCoordinateCube::GLRender(SoGLRenderAction * action)
{
so_texcoordcube_data * data = pimpl->so_texcoord_get_data();
data->currentstate = action->getState();
data->currentshape = NULL;
int unit = SoTextureUnitElement::get(data->currentstate);
if (unit == 0) {
SoTextureCoordinateElement::setFunction(data->currentstate,
this, textureCoordinateCubeCallback,
PRIVATE(this));
}
else {
const cc_glglue * glue = cc_glglue_instance(SoGLCacheContextElement::get(action->getState()));
int maxunits = cc_glglue_max_texture_units(glue);
if (unit < maxunits) {
SoMultiTextureCoordinateElement::setFunction(data->currentstate, this,
unit, textureCoordinateCubeCallback,
PRIVATE(this));
}
}
}
// Documented in superclass.
void
SoTextureCoordinateCube::callback(SoCallbackAction * action)
{
SoTextureCoordinateCube::doAction((SoAction *)action);
}
// Documented in superclass.
void
SoTextureCoordinateCube::pick(SoPickAction * action)
{
SoTextureCoordinateCube::doAction((SoAction *)action);
}