/**************************************************************************\
*
* 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 SoTextureCoordinateSphere include/Inventor/nodes/SoTextureCoordinateSphere.h
\brief The SoTextureCoordinateSphere class autogenerates spheremapped texture coordinated for shapes.
\ingroup nodes
FILE FORMAT/DEFAULTS:
\code
TextureCoordinateSphere {
}
\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_texcoordsphere_data;
static void
so_texcoordsphere_construct_data(void * closure)
{
so_texcoordsphere_data * data = (so_texcoordsphere_data *) closure;
data->currentshape = NULL;
data->currentstate = NULL;
data->origo = SbVec3f(0,0,0);
}
static void
so_texcoordsphere_destruct_data(void * closure)
{
}
SO_NODE_SOURCE(SoTextureCoordinateSphere);
class SoTextureCoordinateSphereP {
public:
SoTextureCoordinateSphereP(SoTextureCoordinateSphere * texturenode)
: master(texturenode) { }
SbVec4f calculateTextureCoordinate(SbVec3f point, SbVec3f n);
so_texcoordsphere_data * so_texcoord_get_data() {
so_texcoordsphere_data * data = NULL;
data = (so_texcoordsphere_data *) this->so_texcoord_storage->get();
assert(data && "Error retrieving thread data.");
return data;
}
SbStorage * so_texcoord_storage;
private:
SoTextureCoordinateSphere * master;
};
static const SbVec4f & textureCoordinateSphereCallback(void * userdata, const SbVec3f & point, const SbVec3f & normal);
#undef PRIVATE
#undef PUBLIC
#define PRIVATE(p) (p->pimpl)
#define PUBLIC(p) (p->master)
/*!
Constructor.
*/
SoTextureCoordinateSphere::SoTextureCoordinateSphere(void)
{
PRIVATE(this) = new SoTextureCoordinateSphereP(this);
SO_NODE_INTERNAL_CONSTRUCTOR(SoTextureCoordinateSphere);
pimpl->so_texcoord_storage = new SbStorage(sizeof(so_texcoordsphere_data),
so_texcoordsphere_construct_data,
so_texcoordsphere_destruct_data);
}
/*!
Destructor.
*/
SoTextureCoordinateSphere::~SoTextureCoordinateSphere()
{
delete pimpl->so_texcoord_storage;
delete PRIVATE(this);
}
// Documented in superclass.
void
SoTextureCoordinateSphere::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoTextureCoordinateSphere, SO_FROM_COIN_2_3);
SO_ENABLE(SoGLRenderAction, SoGLTextureCoordinateElement);
SO_ENABLE(SoCallbackAction, SoTextureCoordinateElement);
SO_ENABLE(SoPickAction, SoTextureCoordinateElement);
}
const SbVec4f &
textureCoordinateSphereCallback(void * userdata,
const SbVec3f & point,
const SbVec3f & normal)
{
SoTextureCoordinateSphereP * pimpl = (SoTextureCoordinateSphereP *) userdata;
so_texcoordsphere_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 && "TextureCoordinateSphere 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
SoTextureCoordinateSphereP::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((float) (atan2(point[0], point[2]) * (1.0/(2.0*M_PI)) + 0.5),
(float) (atan2(point[1], sqrt(point[0]*point[0] + point[2]*point[2])) * (1.0/M_PI) + 0.5),
0.0f, 1.0f);
return tc;
}
// Documented in superclass.
void
SoTextureCoordinateSphere::doAction(SoAction * action)
{
}
// Documented in superclass.
void
SoTextureCoordinateSphere::GLRender(SoGLRenderAction * action)
{
so_texcoordsphere_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, textureCoordinateSphereCallback,
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, textureCoordinateSphereCallback,
PRIVATE(this));
}
}
}
// Documented in superclass.
void
SoTextureCoordinateSphere::callback(SoCallbackAction * action)
{
SoTextureCoordinateSphere::doAction((SoAction *)action);
}
// Documented in superclass.
void
SoTextureCoordinateSphere::pick(SoPickAction * action)
{
SoTextureCoordinateSphere::doAction((SoAction *)action);
}