/**************************************************************************\
*
* 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 SoTextureCoordinateCylinder include/Inventor/nodes/SoTextureCoordinateCylinder.h
\brief The SoTextureCoordinateCylinder class autogenerates cylinder mapped texture coordinated for shapes.
\ingroup nodes
FILE FORMAT/DEFAULTS:
\code
TextureCoordinateCylinder {
}
\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_texcoordcylinder_data;
static void
so_texcoordcylinder_construct_data(void * closure)
{
so_texcoordcylinder_data * data = (so_texcoordcylinder_data *) closure;
data->currentshape = NULL;
data->currentstate = NULL;
data->origo = SbVec3f(0,0,0);
}
static void
so_texcoordcylinder_destruct_data(void * closure)
{
}
// *************************************************************************
class SoTextureCoordinateCylinderP {
public:
SoTextureCoordinateCylinderP(SoTextureCoordinateCylinder * texturenode)
: master(texturenode) { }
SbVec4f calculateTextureCoordinate(SbVec3f point, SbVec3f n);
so_texcoordcylinder_data * so_texcoord_get_data() {
so_texcoordcylinder_data * data = NULL;
data = (so_texcoordcylinder_data *) this->so_texcoord_storage->get();
assert(data && "Error retrieving thread data.");
return data;
}
SbStorage * so_texcoord_storage;
private:
SoTextureCoordinateCylinder * master;
};
static const SbVec4f & textureCoordinateCylinderCallback(void * userdata, const SbVec3f & point, const SbVec3f & normal);
#undef PRIVATE
#undef PUBLIC
#define PRIVATE(p) (p->pimpl)
#define PUBLIC(p) (p->master)
// *************************************************************************
SO_NODE_SOURCE(SoTextureCoordinateCylinder);
// *************************************************************************
/*!
Constructor.
*/
SoTextureCoordinateCylinder::SoTextureCoordinateCylinder(void)
{
PRIVATE(this) = new SoTextureCoordinateCylinderP(this);
SO_NODE_INTERNAL_CONSTRUCTOR(SoTextureCoordinateCylinder);
pimpl->so_texcoord_storage = new SbStorage(sizeof(so_texcoordcylinder_data),
so_texcoordcylinder_construct_data,
so_texcoordcylinder_destruct_data);
}
/*!
Destructor.
*/
SoTextureCoordinateCylinder::~SoTextureCoordinateCylinder()
{
delete pimpl->so_texcoord_storage;
delete PRIVATE(this);
}
// Documented in superclass.
void
SoTextureCoordinateCylinder::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoTextureCoordinateCylinder, SO_FROM_COIN_2_3);
SO_ENABLE(SoGLRenderAction, SoGLTextureCoordinateElement);
SO_ENABLE(SoCallbackAction, SoTextureCoordinateElement);
SO_ENABLE(SoPickAction, SoTextureCoordinateElement);
}
const SbVec4f &
textureCoordinateCylinderCallback(void * userdata,
const SbVec3f & point,
const SbVec3f & normal)
{
SoTextureCoordinateCylinderP * pimpl = (SoTextureCoordinateCylinderP *) userdata;
so_texcoordcylinder_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 && "TextureCoordinateCylinder 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;
}
const SbVec4f & ret = pimpl->calculateTextureCoordinate(point, normal);
data->texcoordreturn = ret;
return data->texcoordreturn;
}
SbVec4f
SoTextureCoordinateCylinderP::calculateTextureCoordinate(SbVec3f point, SbVec3f n)
{
// FIXME: This way of mapping will always lead to artifacts in the
// change between 360 and 0 degrees around the Y-axis. This is
// unavoidable as the callback cannot predict when the last vertex
// will be received, and therefore be able to patch up the
// transition. (20040127 handegar)
SbVec4f tc;
so_texcoordcylinder_data * data = this->so_texcoord_get_data();
const SbVec3f bmax = data->boundingbox.getMax();
const SbVec3f bmin = data->boundingbox.getMin();
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; }
if (maxi == 1) { // Cylinder top or bottom?
// FIXME: A nicer solution might be to calculate the angle between
// the origo<->point and the origo<->bboxcorner before deciding whether this
// is the cylinder top/bottom or not. (20040127 handegar)
float d0 = bmax[2] - bmin[2];
float d1 = bmax[0] - bmin[0];
if (d0 == 0.0f) d0 = 1.0f;
if (d1 == 0.0f) d1 = 1.0f;
tc = SbVec4f((point[0] - bmin[0]) / d1,
(point[2] - bmin[2]) / d0,
0.0f, 1.0f);
if (n[maxi] > 0.0f) tc[1] = 1.0f - tc[1];
}
else {
float d = bmax[1] - bmin[1];
if (d == 0.0f) d = 1.0f;
tc = SbVec4f((float) (atan2(point[0], point[2]) * (1.0/(2.0*M_PI)) + 0.5),
(point[1] - bmin[1]) / d,
0.0f, 1.0f);
}
return tc;
}
// Documented in superclass.
void
SoTextureCoordinateCylinder::doAction(SoAction * action)
{
}
// Documented in superclass.
void
SoTextureCoordinateCylinder::GLRender(SoGLRenderAction * action)
{
so_texcoordcylinder_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, textureCoordinateCylinderCallback,
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, textureCoordinateCylinderCallback,
PRIVATE(this));
}
}
}
// Documented in superclass.
void
SoTextureCoordinateCylinder::callback(SoCallbackAction * action)
{
SoTextureCoordinateCylinder::doAction((SoAction *)action);
}
// Documented in superclass.
void
SoTextureCoordinateCylinder::pick(SoPickAction * action)
{
SoTextureCoordinateCylinder::doAction((SoAction *)action);
}