/**************************************************************************\ * * 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 SoToVRML2Action SoToVRML2Action.h Inventor/actions/SoToVRML2Action.h \brief The SoToVRML2Action class builds a new scene graph, using only VRML97/VRML2 nodes. \ingroup actions This action is used for converting a scene graph of VRML1/Coin nodes to a new scene graph using only VRML97/VRML2 nodes. Due to the basic differences between VRML1/Coin and VRML2 (the latter does not really have a traversal state) the new scene graph will typically be somewhat larger. To minimize this effect the action tries to re-use nodes when possible. VRML1 nodes will be converted to its direct equivalent VRML2 node, while Coin nodes with no VRML2 equivalent are converted to IndexedFaceSet. If the DrawStyle is POINTS, all geometry will be built using PointSet; if it is LINES IndexedLineSet is used. Here's a basic usage example of this action, in the form of a complete, stand-alone program: \code #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { SoDB::init(); SoInteraction::init(); SoInput in; in.openFile(argv[1]); printf("Reading...\n"); SoSeparator *root = SoDB::readAll(&in); if (root) { root->ref(); SbString hdr = in.getHeader(); in.closeFile(); printf("Converting...\n"); SoToVRML2Action tovrml2; tovrml2.apply(root); SoVRMLGroup *newroot = tovrml2.getVRML2SceneGraph(); newroot->ref(); root->unref(); printf("Writing...\n"); SoOutput out; out.openFile("out.wrl"); out.setHeaderString("#VRML V2.0 utf8"); SoWriteAction wra(&out); wra.apply(newroot); out.closeFile(); newroot->unref(); } return 0; } \endcode Note: if VRML97 support is not present in the Coin library, this action does nothing and getVRML2SceneGraph always returns \c NULL. \sa SoToVRMLAction \since Coin 2.0 \since TGS Inventor 2.5 */ // ************************************************************************* // FIXME: SoComplexity::BOUNDING_BOX are not supported. For // DrawStyle::LINES quads are not handled correctly (will always draw // triangles). SoArray and SoMultipleCopy are not supported. // Reusing of appearance and geometry nodes is not implemented. // 20020813 kristian. // ************************************************************************* #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H // default values for cases where a viewport is needed #define DEFAULT_VIEWPORT_WIDTH 1024 #define DEFAULT_VIEWPORT_HEIGHT 768 // ************************************************************************* SO_ACTION_SOURCE(SoToVRML2Action); // ************************************************************************* // helper function needed to copy the name of a node static SoNode * tovrml2_new_node(SoNode * newnode, const SoNode * oldnode) { const SbName name = oldnode->getName(); if (name != SbName("")) newnode->setName(name); return newnode; } // We use SoType::createInstance() instead of simply new'ing to make // an instance, as this makes SoType::overrideType() influence the // conversion process. #define NEW_NODE(_type_, _oldnode_) \ (_type_*) tovrml2_new_node((SoNode *)_type_::getClassTypeId().createInstance(), \ _oldnode_) // ************************************************************************* // Overridden from parent class. void SoToVRML2Action::initClass(void) { SO_ACTION_INTERNAL_INIT_CLASS(SoToVRML2Action, SoToVRMLAction); } /*! \fn SoToVRML2Action::SoToVRML2Action(void) Constructor. */ /*! \fn SoToVRML2Action::~SoToVRML2Action(void) The destructor. */ /*! \fn SoVRMLGroup * SoToVRML2Action::getVRML2SceneGraph(void) const Return a pointer to the root node of the generated scenegraph of only VRML2 / VRML97 nodes. Will return \c NULL if VRML97 support was not compiled into the library. */ /*! \fn void SoToVRML2Action::reuseAppearanceNodes(SbBool appearance) Set the flag deciding if appearance nodes should be reused if possible. The default is FALSE. Please note that support for reusing Appearance nodes is not implemented yet. */ /*! \fn SbBool SoToVRML2Action::doReuseAppearanceNodes(void) const Get the flag deciding if appearance nodes should be reused if possible. The default is FALSE. Please note that support for reusing Appearance nodes is not implemented yet, so this method will always return FALSE. */ /*! \fn void SoToVRML2Action::reusePropertyNodes(SbBool property) Set the flag deciding if property nodes should be reused if possible. The default is FALSE. */ /*! \fn SbBool SoToVRML2Action::doReusePropertyNodes(void) const Get the flag deciding if property nodes should be reused if possible. The default is FALSE. */ /*! \fn void SoToVRML2Action::reuseGeometryNodes(SbBool geometry) Set the flag deciding if geometry nodes should be reused if possible. The default is FALSE. Please note that support for reusing Geometry nodes is not implemented yet. */ /*! \fn SbBool SoToVRML2Action::doReuseGeometryNodes(void) const Get the flag deciding if geometry nodes should be reused if possible. The default is FALSE. Please note that support for reusing Geometry nodes is not implemented yet, so this function will always return FALSE. */ // ************************************************************************* #ifndef HAVE_VRML97 SoToVRML2Action::SoToVRML2Action(void) { SO_ACTION_CONSTRUCTOR(SoToVRML2Action); } SoToVRML2Action::~SoToVRML2Action() { } void SoToVRML2Action::apply(SoNode * node) { } void SoToVRML2Action::apply(SoPath * path) { } void SoToVRML2Action::apply(const SoPathList & pathlist, SbBool obeysrules) { } SoVRMLGroup * SoToVRML2Action::getVRML2SceneGraph(void) const { return NULL; } void SoToVRML2Action::beginTraversal(SoNode * node) { } void SoToVRML2Action::reuseAppearanceNodes(SbBool appearance) { } SbBool SoToVRML2Action::doReuseAppearanceNodes(void) const { return FALSE; } void SoToVRML2Action::reusePropertyNodes(SbBool property) { } SbBool SoToVRML2Action::doReusePropertyNodes(void) const { return FALSE; } void SoToVRML2Action::reuseGeometryNodes(SbBool geometry) { } SbBool SoToVRML2Action::doReuseGeometryNodes(void) const { return FALSE; } #else // HAVE_VRML97 #include #include #ifndef DOXYGEN_SKIP_THIS class SoToVRML2ActionP { public: SoToVRML2ActionP(SoToVRML2Action * masterptr) { this->master = masterptr; this->nodefuse = FALSE; // for optimizing bad scene graphs this->reuseAppearanceNodes = FALSE; this->reusePropertyNodes = FALSE; this->reuseGeometryNodes = FALSE; this->vrml2path = NULL; this->vrml2root = NULL; this->bboxaction = NULL; } void init(void) { this->bsptree = NULL; this->bsptreetex = NULL; this->bsptreenormal = NULL; this->coordidx = NULL; this->normalidx = NULL; this->texidx = NULL; this->coloridx = NULL; recentTex2 = NULL; do_post_primitives = FALSE; didpush = FALSE; this->vrmlcoords = new SbList ; this->vrmlnormals = new SbList ; this->vrmlcolors = new SbList ; this->vrmltexcoords = new SbList ; if (this->vrml2path) { this->vrml2path->unref(); } this->vrml2path = (SoFullPath*) new SoPath; this->vrml2path->ref(); if (this->vrml2root) { this->vrml2root->unref(); } this->vrml2root = new SoVRMLGroup; this->vrml2root->ref(); this->vrml2path->setHead(this->vrml2root); } SoGetBoundingBoxAction * getBBoxAction(void) { if (this->bboxaction == NULL) { SbViewportRegion vp(DEFAULT_VIEWPORT_WIDTH, DEFAULT_VIEWPORT_HEIGHT); this->bboxaction = new SoGetBoundingBoxAction(vp); } return this->bboxaction; } float getBBoxDistance(const SbViewVolume & vv, const float screenarea, const float h) { const float h2 = h * 0.5f; // use half the height for simplicity float screenheight = float(sqrt(screenarea)) * 0.5f; // wanted height in pixels float vvheight = vv.getHeight() * 0.5f; // total height of view volume float neardist = vv.getNearDist(); float projheight = (screenheight / 768.0f) * vvheight; // wanted projected height // now, find the distance the bbox must be at the achieve this projheight if (projheight > 0.0f) { return (neardist / projheight) * h2; } return FLT_MAX; // never switch } SoToVRML2Action * master; SbBool nodefuse; SbBool reuseAppearanceNodes; SbBool reusePropertyNodes; SbBool reuseGeometryNodes; SbHash dict; SoCallbackAction cbaction; SoSearchAction searchaction; SbBSPTree * bsptree; SbBSPTree * bsptreetex; SbBSPTree * bsptreenormal; SbList * coordidx; SbList * normalidx; SbList * texidx; SbList * coloridx; SoGetBoundingBoxAction * bboxaction; SoTexture2 * recentTex2; SbBool do_post_primitives; SbBool didpush; static SoCallbackAction::Response unsupported_cb(void *, SoCallbackAction *, const SoNode *); SoFullPath * vrml2path; SoVRMLGroup * vrml2root; SbList * vrmlcoords; SbList * vrmlnormals; SbList * vrmlcolors; SbList * vrmltexcoords; SoNode * search_for_recent_node(SoAction * action, const SoType & type); SoGroup * get_current_tail(void); SoVRMLCoordinate * get_or_create_coordinate(const SbVec4f *, int32_t num); SoVRMLCoordinate * get_or_create_coordinate(const SbVec3f *, int32_t num); SoVRMLNormal * get_or_create_normal(const SbVec3f *, int32_t num); SoVRMLColor * get_or_create_color(const uint32_t * packedColor, int32_t num); SoVRMLColor * get_or_create_color(const SbColor *, int32_t num); SoVRMLTextureCoordinate * get_or_create_texcoordinate(const SbVec2f *, int32_t num); void insert_shape(SoCallbackAction * action, SoVRMLGeometry * geom); // Shape nodes static SoCallbackAction::Response soasciitext_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response socone_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response socube_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response socylinder_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response soifs_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response soils_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response solineset_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sopointset_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sosphere_cb(void *, SoCallbackAction *, const SoNode *); // Property nodes static SoCallbackAction::Response soinfo_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response somattrans_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sorotation_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sorotationxyz_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response soscale_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sotransform_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sotranslation_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sounits_cb(void *, SoCallbackAction *, const SoNode *); // Group nodes static SoCallbackAction::Response push_sep_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response pop_sep_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response push_switch_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response pop_switch_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response push_lod_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response push_levelofdetail_cb(void *, SoCallbackAction *, const SoNode *); // Other nodes static SoCallbackAction::Response sopercam_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sodirlight_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response sowwwinl_cb(void *, SoCallbackAction *, const SoNode *); // Convert nodes to SoVRMLIndexedFaceSet via triangle cb static SoCallbackAction::Response sotoifs_cb(void *, SoCallbackAction *, const SoNode *); static SoCallbackAction::Response post_primitives_cb(void *, SoCallbackAction *, const SoNode *); static void triangle_cb(void * userdata, SoCallbackAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2, const SoPrimitiveVertex * v3); }; #endif // DOXYGEN_SKIP_THIS #undef PRIVATE #undef PUBLIC #undef THISP #define PRIVATE(p) (p->pimpl) #define PUBLIC(p) (p->master) #define THISP(p) ((SoToVRML2ActionP*)p) // ************************************************************************* // add type and all shapes inheriting type to the list of shapes // already handled by the action static void add_shape_handled(const SoType & type, SoTypeList & addlist) { SoTypeList shapes; (void) SoType::getAllDerivedFrom(type, shapes); int i; for (i = 0; i < shapes.getLength(); i++) { SoType s = shapes[i]; if (s.canCreateInstance() && (addlist.find(s) < 0)) { addlist.append(s); } } } // ************************************************************************* SoToVRML2Action::SoToVRML2Action(void) { SO_ACTION_CONSTRUCTOR(SoToVRML2Action); PRIVATE(this) = new SoToVRML2ActionP(this); #define ADD_PRE_CB(_node_, _cb_) \ PRIVATE(this)->cbaction.addPreCallback(_node_::getClassTypeId(), SoToVRML2ActionP::_cb_, PRIVATE(this)) #define ADD_POST_CB(_node_, _cb_) \ PRIVATE(this)->cbaction.addPostCallback(_node_::getClassTypeId(), SoToVRML2ActionP::_cb_, PRIVATE(this)) #define ADD_UNSUPPORTED(_node_) \ PRIVATE(this)->cbaction.addPreCallback(_node_::getClassTypeId(), SoToVRML2ActionP::unsupported_cb, PRIVATE(this)) #define ADD_TRIANGLE_CB(_node_) \ PRIVATE(this)->cbaction.addTriangleCallback(_node_::getClassTypeId(), SoToVRML2ActionP::triangle_cb, PRIVATE(this)) #define ADD_SHAPE_CB(_node_, _cb_) \ ADD_PRE_CB(_node_, _cb_); ADD_TRIANGLE_CB(_node_); ADD_POST_CB(_node_, post_primitives_cb); \ add_shape_handled(_node_::getClassTypeId(), shapehandledlist); #define ADD_SO_TO_IFS(_node_) \ ADD_PRE_CB(_node_, sotoifs_cb); ADD_TRIANGLE_CB(_node_); ADD_POST_CB(_node_, post_primitives_cb); \ add_shape_handled(_node_::getClassTypeId(), shapehandledlist); SoTypeList shapehandledlist; ADD_SHAPE_CB(SoAsciiText, soasciitext_cb); ADD_SHAPE_CB(SoCone, socone_cb); ADD_SHAPE_CB(SoCube, socube_cb); ADD_SHAPE_CB(SoCylinder, socylinder_cb); ADD_SHAPE_CB(SoIndexedFaceSet, soifs_cb); ADD_SHAPE_CB(SoIndexedLineSet, soils_cb); ADD_SHAPE_CB(SoPointSet, sopointset_cb); ADD_SHAPE_CB(SoSphere, sosphere_cb); // Property nodes ADD_PRE_CB(SoInfo, soinfo_cb); ADD_PRE_CB(SoMatrixTransform, somattrans_cb); ADD_PRE_CB(SoRotation, sorotation_cb); ADD_PRE_CB(SoRotationXYZ, sorotationxyz_cb); ADD_PRE_CB(SoScale, soscale_cb); ADD_PRE_CB(SoTransform, sotransform_cb); ADD_PRE_CB(SoTranslation, sotranslation_cb); ADD_PRE_CB(SoUnits, sounits_cb); // Group nodes ADD_PRE_CB(SoVRMLGroup, push_sep_cb); // support for VRML97 Transform and Group ADD_POST_CB(SoVRMLGroup, pop_sep_cb); ADD_PRE_CB(SoSeparator, push_sep_cb); ADD_POST_CB(SoSeparator, pop_sep_cb); ADD_PRE_CB(SoSwitch, push_switch_cb); ADD_POST_CB(SoSwitch, pop_switch_cb); ADD_PRE_CB(SoLOD, push_lod_cb); ADD_PRE_CB(SoLevelOfDetail, push_levelofdetail_cb); ADD_UNSUPPORTED(SoWWWAnchor); // Convert to SoVRMLAnchor // Other nodes ADD_UNSUPPORTED(SoOrthographicCamera); ADD_PRE_CB(SoPerspectiveCamera, sopercam_cb); ADD_PRE_CB(SoDirectionalLight, sodirlight_cb); ADD_UNSUPPORTED(SoPointLight); ADD_UNSUPPORTED(SoSpotLight); ADD_PRE_CB(SoWWWInline, sowwwinl_cb); // Coin nodes ADD_SHAPE_CB(SoLineSet, solineset_cb); ADD_SO_TO_IFS(SoIndexedTriangleStripSet); ADD_SO_TO_IFS(SoFaceSet); ADD_SO_TO_IFS(SoQuadMesh); ADD_SO_TO_IFS(SoTriangleStripSet); ADD_SO_TO_IFS(SoNurbsCurve); ADD_SO_TO_IFS(SoNurbsSurface); ADD_SO_TO_IFS(SoIndexedNurbsCurve); ADD_SO_TO_IFS(SoIndexedNurbsSurface); // find all shapes not handled earlier, and add generic triangle // handling for them // // FIXME: also add line segment callback and point callback. pederb, // 2005-06-10 SoTypeList shapes; (void) SoType::getAllDerivedFrom(SoShape::getClassTypeId(), shapes); int i; for (i = 0; i < shapes.getLength(); i++) { SoType type = shapes[i]; if (type.canCreateInstance() && (shapehandledlist.find(type) < 0)) { PRIVATE(this)->cbaction.addPreCallback(type, SoToVRML2ActionP::sotoifs_cb, PRIVATE(this)); PRIVATE(this)->cbaction.addTriangleCallback(type, SoToVRML2ActionP::triangle_cb, PRIVATE(this)); PRIVATE(this)->cbaction.addPostCallback(type, SoToVRML2ActionP::post_primitives_cb, PRIVATE(this)); } } #undef ADD_PRE_CB #undef ADD_POST_CB #undef ADD_UNSUPPORTED #undef ADD_TRIANGLE_CB #undef ADD_SO_TO_IFS } SoToVRML2Action::~SoToVRML2Action(void) { if (PRIVATE(this)->bboxaction) { delete PRIVATE(this)->bboxaction; } if (PRIVATE(this)->vrml2path) { PRIVATE(this)->vrml2path->unref(); } if (PRIVATE(this)->vrml2root) { PRIVATE(this)->vrml2root->unref(); } delete PRIVATE(this); } // Documented in superclass. void SoToVRML2Action::apply(SoNode * root) { PRIVATE(this)->init(); PRIVATE(this)->cbaction.apply(root); } // Documented in superclass. void SoToVRML2Action::apply(SoPath * path) { PRIVATE(this)->init(); PRIVATE(this)->cbaction.apply(path); } // Documented in superclass. void SoToVRML2Action::apply(const SoPathList & pathlist, SbBool obeysrules) { PRIVATE(this)->init(); PRIVATE(this)->cbaction.apply(pathlist, obeysrules); } // Documented in superclass. void SoToVRML2Action::beginTraversal(SoNode * node) { assert(0 && "should never get here"); } SoVRMLGroup * SoToVRML2Action::getVRML2SceneGraph(void) const { return PRIVATE(this)->vrml2root; } void SoToVRML2Action::reuseAppearanceNodes(SbBool appearance) { // FIXME: not implemented yet. 20020808 mortene. } SbBool SoToVRML2Action::doReuseAppearanceNodes(void) const { // FIXME: not implemented yet. 20020808 mortene. return FALSE; } void SoToVRML2Action::reusePropertyNodes(SbBool property) { PRIVATE(this)->reusePropertyNodes = property; } SbBool SoToVRML2Action::doReusePropertyNodes(void) const { return PRIVATE(this)->reusePropertyNodes; } void SoToVRML2Action::reuseGeometryNodes(SbBool geometry) { // FIXME: not implemented yet. 20020808 mortene. } SbBool SoToVRML2Action::doReuseGeometryNodes(void) const { // FIXME: not implemented yet. 20020808 mortene. return FALSE; } SoNode * SoToVRML2ActionP::search_for_recent_node(SoAction * action, const SoType & type) { this->searchaction.setSearchingAll(FALSE); this->searchaction.setType(type); this->searchaction.setInterest(SoSearchAction::LAST); SbBool old = SoBaseKit::isSearchingChildren(); SoBaseKit::setSearchingChildren(TRUE); this->searchaction.apply((SoPath *)action->getCurPath()); SoNode * tail = NULL; SoFullPath * path = (SoFullPath*) this->searchaction.getPath(); if (path) { tail = path->getTail(); } this->searchaction.reset(); SoBaseKit::setSearchingChildren(old); return tail; } SoGroup * SoToVRML2ActionP::get_current_tail(void) { SoNode * node = this->vrml2path->getTail(); assert(node->isOfType(SoVRMLGroup::getClassTypeId()) || node->isOfType(SoVRMLSwitch::getClassTypeId()) || node->isOfType(SoVRMLLOD::getClassTypeId())); return (SoGroup*) node; } SoVRMLCoordinate * SoToVRML2ActionP::get_or_create_coordinate(const SbVec4f * coord4, int32_t num) { SbList vec3f; for (int i = 0; i < num; i++) { SbVec3f tmp; coord4[i].getReal(tmp); vec3f.append(tmp); } return this->get_or_create_coordinate(vec3f.getArrayPtr(), num); } SoVRMLCoordinate * SoToVRML2ActionP::get_or_create_coordinate(const SbVec3f * coord3, int32_t num) { if (this->reusePropertyNodes) { // Search for a matching VRMLCoordinate int n = this->vrmlcoords->getLength(); while (--n >= 0) { SoVRMLCoordinate * c = (*this->vrmlcoords)[n]; if (c->point.getNum() == num && memcmp(coord3, c->point.getValues(0), num*sizeof(SbVec3f)) == 0) { return c; } } } // Create new SoVRMLCoordinate * c = new SoVRMLCoordinate; c->point.setValues(0, num, coord3); if (this->reusePropertyNodes) this->vrmlcoords->append(c); return c; } SoVRMLNormal * SoToVRML2ActionP::get_or_create_normal(const SbVec3f * normal, int32_t num) { if (this->reusePropertyNodes) { // Search for a matching VRMLNormal int n = this->vrmlnormals->getLength(); while (--n >= 0) { SoVRMLNormal * nor = (*this->vrmlnormals)[n]; if (nor->vector.getNum() == num && memcmp(normal, nor->vector.getValues(0), num*sizeof(SbVec3f)) == 0) { return nor; } } } // Create new SoVRMLNormal * nor = new SoVRMLNormal; nor->vector.setValues(0, num, normal); if (this->reusePropertyNodes) this->vrmlnormals->append(nor); return nor; } SoVRMLColor * SoToVRML2ActionP::get_or_create_color(const uint32_t * packedColor, int32_t num) { // Convert to SbColors SbList color; float f; for (int i = 0; i < num; i++) { SbColor tmp; tmp.setPackedValue(packedColor[i], f); color.append(tmp); } return this->get_or_create_color(color.getArrayPtr(), num); } SoVRMLColor * SoToVRML2ActionP::get_or_create_color(const SbColor * color, int32_t num) { if (this->reusePropertyNodes) { // Search for a matching VRMLColor int n = this->vrmlcolors->getLength(); while (--n >= 0) { SoVRMLColor * c = (*this->vrmlcolors)[n]; if (c->color.getNum() == num && memcmp(color, c->color.getValues(0), num*sizeof(SbColor)) == 0) { return c; } } } // Create new SoVRMLColor * c = new SoVRMLColor; c->color.setValues(0, num, color); if (this->reusePropertyNodes) this->vrmlcolors->append(c); return c; } SoVRMLTextureCoordinate * SoToVRML2ActionP::get_or_create_texcoordinate(const SbVec2f * texcoord2, int32_t num) { if (this->reusePropertyNodes) { // Search for a matching VRMLTextureCoordinate int n = this->vrmltexcoords->getLength(); while (--n >= 0) { SoVRMLTextureCoordinate * tc = (*this->vrmltexcoords)[n]; if (tc->point.getNum() == num && memcmp(texcoord2, tc->point.getValues(0), num*sizeof(SbVec2f)) == 0) { return tc; } } } // Create new SoVRMLTextureCoordinate * tc = new SoVRMLTextureCoordinate; tc->point.setValues(0, num, texcoord2); if (this->reusePropertyNodes) this->vrmltexcoords->append(tc); return tc; } void SoToVRML2ActionP::insert_shape(SoCallbackAction * action, SoVRMLGeometry * geom) { SoVRMLShape * shape = new SoVRMLShape; shape->geometry = geom; // Create appearance SoVRMLAppearance * appearance = new SoVRMLAppearance; shape->appearance = appearance; SoVRMLMaterial * mat = new SoVRMLMaterial; appearance->material = mat; // Get values from current state SbColor ambient, diffuse, specular, emissions; float shin, transp; action->getMaterial(ambient, diffuse, specular, emissions, shin, transp); if (!geom->isOfType(SoVRMLPointSet::getClassTypeId())) { if (mat->diffuseColor.getValue() != diffuse) mat->diffuseColor = diffuse; // Convert to grayscale for calculating the ambient intensity float ambientGray = ambient[0] * 77 + ambient[1] * 150 + ambient[2] * 29; if (ambientGray > 0) { float ambientIntensity = SbMin(1.0f, ambientGray / 256.0f); if (mat->ambientIntensity.getValue() != ambientIntensity) mat->ambientIntensity = ambientIntensity; } if (mat->specularColor.getValue() != specular) mat->specularColor = specular; if (mat->emissiveColor.getValue() != emissions) mat->emissiveColor = emissions; if (mat->shininess.getValue() != shin) mat->shininess = shin; if (mat->transparency.getValue() != transp) mat->transparency = transp; // Texture if (this->recentTex2 == NULL) { this->recentTex2 = (SoTexture2 *) search_for_recent_node(action, SoTexture2::getClassTypeId()); } if (this->recentTex2 != NULL) { SbVec2s size; int numComponents; const unsigned char * image = this->recentTex2->image.getValue(size, numComponents); if (!this->recentTex2->filename.isDefault() || (size[0] > 0 && size[1] > 0)) { SoVRMLTexture * tex; if (!this->recentTex2->filename.isDefault()) { tex = new SoVRMLImageTexture; SbString url = this->master->getUrlName(); url += this->recentTex2->filename.getValue(); ((SoVRMLImageTexture *)tex)->url.setValue(url); } else { tex = new SoVRMLPixelTexture; ((SoVRMLPixelTexture *)tex)->image.setValue(size, numComponents, image); } tex->repeatS = this->recentTex2->wrapS.getValue() == SoTexture2::REPEAT; tex->repeatT = this->recentTex2->wrapT.getValue() == SoTexture2::REPEAT; appearance->texture = tex; // Texture transform const SbMatrix * matrix = &action->getTextureMatrix(); if (!matrix->equals(SbMatrix::identity(), 0.0f)) { SbVec3f translation, scaleFactor; SbRotation rotation, scaleOrientation; matrix->getTransform(translation, rotation, scaleFactor, scaleOrientation); SoVRMLTextureTransform * textrans = new SoVRMLTextureTransform; textrans->translation = SbVec2f(translation[0], translation[1]); SbVec3f axis; float radians; rotation.getValue(axis, radians); if (axis[2] < 0) radians = 2.0f*(float)M_PI - radians; textrans->rotation = radians; textrans->scale = SbVec2f(scaleFactor[0], scaleFactor[1]); appearance->textureTransform = textrans; } } this->recentTex2 = NULL; } } else { if (mat->emissiveColor.getValue() != diffuse) mat->emissiveColor = diffuse; } get_current_tail()->addChild(shape); } SoCallbackAction::Response SoToVRML2ActionP::push_sep_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; SoGroup * prevgroup = THISP(closure)->get_current_tail(); SoGroup * vp; if (THISP(closure)->dict.get(node, vp)) { // Re-use previous subgraph prevgroup->addChild(vp); return SoCallbackAction::PRUNE; } // Push a new SoVRMLGroup on the tail of the path SoVRMLGroup * newgroup = NULL; if (node->isOfType(SoVRMLTransform::getClassTypeId())) { SoVRMLTransform * oldtrans = (SoVRMLTransform*) node; SoVRMLTransform * newtrans = NEW_NODE(SoVRMLTransform, node); newgroup = newtrans; newtrans->translation = oldtrans->translation; newtrans->rotation = oldtrans->rotation; newtrans->scale = oldtrans->scale; newtrans->scaleOrientation = oldtrans->scaleOrientation; newtrans->center = oldtrans->center; } else { newgroup = NEW_NODE(SoVRMLGroup, node); } // Push a new SoVRMLGroup on the tail of the path prevgroup->addChild(newgroup); thisp->vrml2path->append(newgroup); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::pop_sep_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoGroup * vp; if (THISP(closure)->dict.get(node, vp)) { return SoCallbackAction::CONTINUE; } // Pop node from the tail of the path until an SoVRMLGroup has been popped SoGroup * grp; do { grp = THISP(closure)->get_current_tail(); THISP(closure)->vrml2path->pop(); } while (!grp->isOfType(SoVRMLGroup::getClassTypeId())); THISP(closure)->dict.put(node, grp); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::push_switch_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; SoGroup * prevgroup = THISP(closure)->get_current_tail(); SoGroup * vp; if (THISP(closure)->dict.get(node, vp)) { // Re-use previous subgraph prevgroup->addChild(vp); return SoCallbackAction::PRUNE; } const SoSwitch * oldswitch = (const SoSwitch *) node; SoVRMLSwitch * newswitch = NEW_NODE(SoVRMLSwitch, node); // SO_SWITCH_INHERIT is not supported in VRML97, so just translate // it here. We could perhaps consider creating a ROUTE from the // inherited whichChoice field to this field... int wc = oldswitch->whichChild.getValue() == SO_SWITCH_INHERIT ? action->getSwitch() : oldswitch->whichChild.getValue(); newswitch->whichChoice = wc; prevgroup->addChild(newswitch); thisp->vrml2path->append(newswitch); /* Traverse all children separately, that is, save and restore state * between each. If there is a selected child, traverse it normally * This is needed so that traversing the not selected * children won't influence the selected child. */ if (wc != SO_SWITCH_ALL) { SoState * state = action->getState(); // update SwitchElement before traversing children (this is // usually done in SoSwitch::doAction) (don't push before setting // this element as it's supposed to be set when traversing the // next sibling). SoSwitchElement::set(state, wc); int n = oldswitch->getNumChildren(); for (int i = 0; i < n; i++) { if (i != wc) { state->push(); action->switchToNodeTraversal(oldswitch->getChild(i)); state->pop(); } else { action->switchToNodeTraversal(oldswitch->getChild(i)); } } // so that the children will not be traversed return SoCallbackAction::PRUNE; } // traverse Switch node as a normal group node return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::pop_switch_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoGroup * vp; if (THISP(closure)->dict.get(node, vp)) { return SoCallbackAction::CONTINUE; } SoGroup * grp; do { grp = THISP(closure)->get_current_tail(); THISP(closure)->vrml2path->pop(); } while (grp->getTypeId() != SoVRMLSwitch::getClassTypeId()); SoVRMLSwitch * sw = (SoVRMLSwitch *) grp; int wc = sw->whichChoice.getValue(); if (wc == SO_SWITCH_ALL) { // workaround since VRML97 does not support SO_SWITCH_ALL. SoVRMLGroup * allfix = new SoVRMLGroup; allfix->ref(); for (int i = 0; i < sw->getNumChoices(); i++) { allfix->addChild(sw->getChoice(i)); } sw->removeAllChoices(); sw->addChoice(allfix); allfix->unrefNoDelete(); // set whichChoice to point to the new group node sw->whichChoice = 0; } THISP(closure)->dict.put(node, grp); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::push_levelofdetail_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; SoGroup * prevgroup = THISP(closure)->get_current_tail(); SoGroup * vp; if (THISP(closure)->dict.get(node, vp)) { // Re-use previous subgraph prevgroup->addChild(vp); return SoCallbackAction::PRUNE; } const SoLevelOfDetail * oldlod = (const SoLevelOfDetail *) node; SoVRMLLOD * newlod = NEW_NODE(SoVRMLLOD, node); // calculate bbox of children to find a reasonable conversion to range SoGetBoundingBoxAction * bboxAction = thisp->getBBoxAction(); SbViewportRegion viewport(DEFAULT_VIEWPORT_WIDTH, DEFAULT_VIEWPORT_HEIGHT); bboxAction->setViewportRegion(viewport); // need to apply on the current path, not on the node, since we // might need coordinates from the state. Also, we need to set the // reset path so that we get the local bounding box for the nodes // below this node. bboxAction->setResetPath(action->getCurPath()); bboxAction->apply((SoPath*) action->getCurPath()); // find bbox of all children SbBox3f bbox = bboxAction->getBoundingBox(); float dx, dy, dz; bbox.getSize(dx,dy,dz); const float h = SbMax(SbMax(dx,dy), dz); // create a typical view volume SbViewVolume vv; vv.perspective(float(M_PI)/4.0f, DEFAULT_VIEWPORT_WIDTH/DEFAULT_VIEWPORT_HEIGHT, 1.0f, 10.0f); newlod->range.setNum(oldlod->screenArea.getNum()); float * rangeptr = newlod->range.startEditing(); int i; for (i = 0; i < oldlod->screenArea.getNum(); i++) { rangeptr[i] = thisp->getBBoxDistance(vv, oldlod->screenArea[i], h); } newlod->range.finishEditing(); prevgroup->addChild(newlod); thisp->vrml2path->append(newlod); // Traverse all children separately, that is, save and restore state between each int n = oldlod->getNumChildren(); for (i=0; i < n; i++) { action->switchToNodeTraversal(oldlod->getChild(i)); } thisp->vrml2path->pop(); THISP(closure)->dict.put(node, newlod); return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::push_lod_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; SoGroup * prevgroup = THISP(closure)->get_current_tail(); SoGroup * vp; if (THISP(closure)->dict.get(node, vp)) { // Re-use previous subgraph prevgroup->addChild(vp); return SoCallbackAction::PRUNE; } const SoLOD * oldlod = (const SoLOD *) node; SoVRMLLOD * newlod = NEW_NODE(SoVRMLLOD, node); newlod->range.setValues(0, oldlod->range.getNum(), oldlod->range.getValues(0)); newlod->center = oldlod->center.getValue(); prevgroup->addChild(newlod); thisp->vrml2path->append(newlod); // Traverse all children separately, that is, save and restore state between each int n = oldlod->getNumChildren(); for (int i=0; i < n; i++) { action->switchToNodeTraversal(oldlod->getChild(i)); } thisp->vrml2path->pop(); THISP(closure)->dict.put(node, newlod); return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::unsupported_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoVRMLWorldInfo * info = NEW_NODE(SoVRMLWorldInfo, node); SbString str; str.sprintf("Unsupported node: %s", node->getTypeId().getName().getString()); info->title = str; THISP(closure)->get_current_tail()->addChild(info); if (THISP(closure)->master->isVerbose()) { SoDebugError::postWarning("SoToVRML2Action::unsupported_cb", "%s", str.getString()); } return SoCallbackAction::CONTINUE; } // Shape nodes SoCallbackAction::Response SoToVRML2ActionP::soasciitext_cb(void * closure, SoCallbackAction * action, const SoNode * node) { if (action->getDrawStyle() != SoDrawStyle::FILLED) { return SoToVRML2ActionP::sotoifs_cb(closure, action, node); } SoVRMLText * text = NEW_NODE(SoVRMLText, node); const SoAsciiText * oldtext = (const SoAsciiText*) node; text->string = oldtext->string; text->length = oldtext->width; SoVRMLFontStyle *style = new SoVRMLFontStyle; style->size.setValue(action->getFontSize()); text->fontStyle.setValue(style); // FIXME: Better FontStyle handling (20030414 kintel) THISP(closure)->insert_shape(action, text); return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::socube_cb(void * closure, SoCallbackAction * action, const SoNode * node) { if (action->getDrawStyle() != SoDrawStyle::FILLED) { return SoToVRML2ActionP::sotoifs_cb(closure, action, node); } SoVRMLBox * box = NEW_NODE(SoVRMLBox, node); const SoCube * cube = (const SoCube*) node; if (box->size.getValue()[0] != cube->width.getValue() || box->size.getValue()[1] != cube->height.getValue() || box->size.getValue()[2] != cube->depth.getValue()) { box->size.setValue(cube->width.getValue(), cube->height.getValue(), cube->depth.getValue()); } THISP(closure)->insert_shape(action, box); return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::socone_cb(void * closure, SoCallbackAction * action, const SoNode * node) { if (action->getDrawStyle() != SoDrawStyle::FILLED) { return SoToVRML2ActionP::sotoifs_cb(closure, action, node); } SoVRMLCone * cone = NEW_NODE(SoVRMLCone, node); const SoCone * oldcone = (const SoCone*) node; if (oldcone->bottomRadius != cone->bottomRadius) cone->bottomRadius = oldcone->bottomRadius.getValue(); if (oldcone->height != cone->height) cone->height = oldcone->height.getValue(); SbBool bottom = (oldcone->parts.getValue() & SoCone::BOTTOM) ? TRUE : FALSE; if (bottom != cone->bottom.getValue()) cone->bottom = bottom; SbBool side = (oldcone->parts.getValue() & SoCone::SIDES) ? TRUE : FALSE; if (side != cone->side.getValue()) cone->side = side; THISP(closure)->insert_shape(action, cone); return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::socylinder_cb(void * closure, SoCallbackAction * action, const SoNode * node) { if (action->getDrawStyle() != SoDrawStyle::FILLED) { return SoToVRML2ActionP::sotoifs_cb(closure, action, node); } SoVRMLCylinder * cyl = NEW_NODE(SoVRMLCylinder, node); const SoCylinder * oldcyl = (const SoCylinder*) node; if (oldcyl->radius != cyl->radius) cyl->radius = oldcyl->radius.getValue(); if (oldcyl->height != cyl->height) cyl->height = oldcyl->height.getValue(); SbBool side = (oldcyl->parts.getValue() & SoCylinder::SIDES) ? TRUE : FALSE; if (side != cyl->side.getValue()) cyl->side = side; SbBool top = (oldcyl->parts.getValue() & SoCylinder::TOP) ? TRUE : FALSE; if (top != cyl->top.getValue()) cyl->top = top; SbBool bottom = (oldcyl->parts.getValue() & SoCylinder::BOTTOM) ? TRUE : FALSE; if (bottom != cyl->bottom.getValue()) cyl->bottom = bottom; THISP(closure)->insert_shape(action, cyl); return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::soifs_cb(void * closure, SoCallbackAction * action, const SoNode * node) { if (action->getDrawStyle() != SoDrawStyle::FILLED) { return SoToVRML2ActionP::sotoifs_cb(closure, action, node); } const SoIndexedFaceSet * oldifs = (const SoIndexedFaceSet*) node; if (oldifs->coordIndex.getNum() == 0 || oldifs->coordIndex[0] < 0) return SoCallbackAction::CONTINUE; SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; SoGroup * tail = thisp->get_current_tail(); SoVRMLIndexedFaceSet * ifs = NEW_NODE(SoVRMLIndexedFaceSet, node); // Set the values from the current ShapeHints ifs->creaseAngle = action->getCreaseAngle(); ifs->ccw = action->getVertexOrdering() != SoShapeHints::CLOCKWISE; ifs->solid = SoShapeHintsElement::getShapeType(action->getState()) == SoShapeHintsElement::SOLID; ifs->convex = action->getFaceType() == SoShapeHints::CONVEX; // If there is a VertexProperty node set we need to put it on the state stack SoNode *vpnode = oldifs->vertexProperty.getValue(); SoVertexProperty *vp = (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ? (SoVertexProperty *)vpnode : NULL; if (vp) { action->getState()->push(); vp->callback(action); } const SoCoordinateElement * coordElem = SoCoordinateElement::getInstance(action->getState()); if (coordElem->getNum() > 0) { if (coordElem->getArrayPtr3() != NULL) { ifs->coord = thisp->get_or_create_coordinate(coordElem->getArrayPtr3(), coordElem->getNum()); } else { ifs->coord = thisp->get_or_create_coordinate(coordElem->getArrayPtr4(), coordElem->getNum()); } } if (action->getNormalBinding() != SoNormalBinding::OVERALL) { const SoNormalElement * normalElem = SoNormalElement::getInstance(action->getState()); if (coordElem->getNum() > 0) { ifs->normal = thisp->get_or_create_normal(normalElem->getArrayPtr(), normalElem->getNum()); if (action->getNormalBinding() != SoNormalBinding::PER_VERTEX_INDEXED && action->getNormalBinding() != SoNormalBinding::PER_VERTEX) { ifs->normalPerVertex = FALSE; } } } if (action->getMaterialBinding() != SoMaterialBinding::OVERALL) { SoLazyElement * lazy = SoLazyElement::getInstance(action->getState()); if (lazy->getNumDiffuse() > 1) { if (lazy->isPacked()) { ifs->color = thisp->get_or_create_color(lazy->getPackedPointer(), lazy->getNumDiffuse()); } else { ifs->color = thisp->get_or_create_color(lazy->getDiffusePointer(), lazy->getNumDiffuse()); } if (action->getMaterialBinding() != SoMaterialBinding::PER_VERTEX_INDEXED && action->getMaterialBinding() != SoMaterialBinding::PER_VERTEX) { ifs->colorPerVertex = FALSE; } } } const SoTextureCoordinateElement * texcoordElem = SoTextureCoordinateElement::getInstance(action->getState()); if (texcoordElem->getNum() > 0) { ifs->texCoord = thisp->get_or_create_texcoordinate(texcoordElem->getArrayPtr2(), texcoordElem->getNum()); } ifs->coordIndex.setValues(0, oldifs->coordIndex.getNum(), oldifs->coordIndex.getValues(0)); if (!oldifs->textureCoordIndex.isDefault() && oldifs->textureCoordIndex.getNum()) { ifs->texCoordIndex.setValues(0, oldifs->textureCoordIndex.getNum(), oldifs->textureCoordIndex.getValues(0)); } if (!oldifs->materialIndex.isDefault() && oldifs->materialIndex.getNum()) { ifs->colorIndex.setValues(0, oldifs->materialIndex.getNum(), oldifs->materialIndex.getValues(0)); } if (!oldifs->normalIndex.isDefault() && oldifs->normalIndex.getNum()) { ifs->normalIndex.setValues(0, oldifs->normalIndex.getNum(), oldifs->normalIndex.getValues(0)); } THISP(closure)->insert_shape(action, ifs); // it's important to pop state _after_ inserting the shape to get // the correct material from SoVertexProperty nodes. if (vp) { action->getState()->pop(); } return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::soils_cb(void * closure, SoCallbackAction * action, const SoNode * node) { // FIXME: test for drawstyle == POINTS and convert to a point set // instead. pederb, 20060327 SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; const SoIndexedLineSet * oldils = (const SoIndexedLineSet*) node; if (oldils->coordIndex.getNum() == 0 || oldils->coordIndex[0] < 0) return SoCallbackAction::CONTINUE; SoVRMLIndexedLineSet * ils = NEW_NODE(SoVRMLIndexedLineSet, node); SoGroup * tail = thisp->get_current_tail(); // If there is a VertexProperty node set we need to put it on the state stack SoNode *vpnode = oldils->vertexProperty.getValue(); SoVertexProperty *vp = (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ? (SoVertexProperty *)vpnode : NULL; if (vp) { action->getState()->push(); vp->callback(action); } SoVRMLCoordinate * newcoord = NULL; const SoCoordinateElement * coordElem = SoCoordinateElement::getInstance(action->getState()); if (coordElem->getNum() > 0) { if (thisp->nodefuse) { newcoord = new SoVRMLCoordinate; } else { if (coordElem->getArrayPtr3() != NULL) { newcoord = thisp->get_or_create_coordinate(coordElem->getArrayPtr3(), coordElem->getNum()); } else { newcoord = thisp->get_or_create_coordinate(coordElem->getArrayPtr4(), coordElem->getNum()); } } ils->coord = newcoord; } if (action->getMaterialBinding() != SoMaterialBinding::OVERALL) { const SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->getNumDiffuse() > 0) { if (colorElem->isPacked()) { ils->color = thisp->get_or_create_color(colorElem->getPackedPointer(), colorElem->getNumDiffuse()); } else { ils->color = thisp->get_or_create_color(colorElem->getDiffusePointer(), colorElem->getNumDiffuse()); } if (action->getMaterialBinding() != SoMaterialBinding::PER_VERTEX_INDEXED && action->getMaterialBinding() != SoMaterialBinding::PER_VERTEX) { ils->colorPerVertex = FALSE; } } } if (thisp->nodefuse && coordElem->getNum() > 0) { SbBSPTree bsp; int n = oldils->coordIndex.getNum(); const int32_t * src = oldils->coordIndex.getValues(0); SbVec3f * c = (SbVec3f*) coordElem->getArrayPtr3(); if (c == NULL) { SbVec3f * vec3f = new SbVec3f[coordElem->getNum()]; const SbVec4f * coord4 = coordElem->getArrayPtr4(); for (int i=coordElem->getNum()-1; i >= 0; i--) { coord4[i].getReal(vec3f[i]); } c = vec3f; } ils->coordIndex.setNum(n); int32_t * dst = ils->coordIndex.startEditing(); for (int i = 0; i < n; i++) { int32_t idx = src[i]; if (idx >= 0) { dst[i] = bsp.addPoint(c[idx]); } else dst[i] = -1; } ils->coordIndex.finishEditing(); newcoord->point.setValues(0, bsp.numPoints(), bsp.getPointsArrayPtr()); if (coordElem->getArrayPtr3() == NULL) delete[] c; } else { ils->coordIndex.setValues(0, oldils->coordIndex.getNum(), oldils->coordIndex.getValues(0)); } if (action->getMaterialBinding() == SoMaterialBinding::PER_VERTEX_INDEXED || action->getMaterialBinding() == SoMaterialBinding::PER_FACE_INDEXED) { ils->colorIndex.setValues(0, oldils->materialIndex.getNum(), oldils->materialIndex.getValues(0)); } else if (action->getMaterialBinding() == SoMaterialBinding::PER_PART_INDEXED || action->getMaterialBinding() == SoMaterialBinding::PER_PART) { // Color per segment, convert to per vertex SbList coordIdx; SbBSPTree bsp; int32_t colidx = 0; SoVRMLColor * color = (SoVRMLColor *)ils->color.getValue(); int n = ils->coordIndex.getNum()-1; for (int i = 0; i < n; i++) { SbVec3f curcol, nextcol; if (action->getMaterialBinding() == SoMaterialBinding::PER_PART_INDEXED) { curcol = color->color[oldils->materialIndex[colidx]]; if (i != n-1) nextcol = color->color[oldils->materialIndex[colidx+1]]; } else { curcol = color->color[colidx]; if (i != n-1) nextcol = color->color[colidx+1]; } colidx++; coordIdx.append(bsp.addPoint(curcol)); if (i == n-1 || ils->coordIndex[i+2] == -1) { // Current polyline is done coordIdx.append(coordIdx[coordIdx.getLength()-1]); coordIdx.append(-1); i += 2; } else if (curcol != nextcol) { // Create a new vertex to avoid color interpolation ils->coordIndex.insertSpace(i+1, 1); ils->coordIndex.set1Value(i+1, ils->coordIndex[i+2]); coordIdx.append(bsp.addPoint(curcol)); i++; n++; } } ils->color = thisp->get_or_create_color((const SbColor *)bsp.getPointsArrayPtr(), bsp.numPoints()); ils->colorIndex.setValues(0, coordIdx.getLength(), coordIdx.getArrayPtr()); ils->colorPerVertex = TRUE; } THISP(closure)->insert_shape(action, ils); // it's important to pop state _after_ inserting the shape to get // the correct material from SoVertexProperty nodes. if (vp) { action->getState()->pop(); } return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::solineset_cb(void * closure, SoCallbackAction * action, const SoNode * node) { // FIXME: test for drawstyle == POINTS and convert to a point set // instead. pederb, 20060327 SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; const SoLineSet * oldls = (const SoLineSet*) node; if (oldls->numVertices.getNum() == 0) return SoCallbackAction::CONTINUE; SoVRMLIndexedLineSet * ils = NEW_NODE(SoVRMLIndexedLineSet, node); SoGroup * tail = thisp->get_current_tail(); // If there is a VertexProperty node set we need to put it on the state stack SoNode *vpnode = oldls->vertexProperty.getValue(); SoVertexProperty *vp = (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ? (SoVertexProperty *)vpnode : NULL; if (vp) { action->getState()->push(); vp->callback(action); } const SoCoordinateElement * coordElem = SoCoordinateElement::getInstance(action->getState()); if (coordElem->getNum() > 0) { if (coordElem->getArrayPtr3() != NULL) { ils->coord = thisp->get_or_create_coordinate(coordElem->getArrayPtr3(), coordElem->getNum()); } else { ils->coord = thisp->get_or_create_coordinate(coordElem->getArrayPtr4(), coordElem->getNum()); } } if (action->getMaterialBinding() != SoMaterialBinding::OVERALL) { const SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->getNumDiffuse() > 0) { if (colorElem->isPacked()) { ils->color = thisp->get_or_create_color(colorElem->getPackedPointer(), colorElem->getNumDiffuse()); } else { ils->color = thisp->get_or_create_color(colorElem->getDiffusePointer(), colorElem->getNumDiffuse()); } if (action->getMaterialBinding() != SoMaterialBinding::PER_VERTEX) { ils->colorPerVertex = FALSE; } } } SbList l; int n = oldls->numVertices.getNum(); int32_t curidx = 0; // check for special case where lineset should render all vertices // on the state if ((n == 1) && (oldls->numVertices[0] == -1)) { const int numv = coordElem->getNum(); for (int i = 0; i < numv; i++) { l.append(curidx++); } l.append(-1); } else { for (int i = 0; i < n; i++) { for (int j = oldls->numVertices[i]-1; j >= 0; j--) { l.append(curidx++); } l.append(-1); } } ils->coordIndex.setValues(0, l.getLength(), l.getArrayPtr()); if (action->getMaterialBinding() == SoMaterialBinding::PER_PART) { // Color per segment, convert to per vertex SbList coordIdx; SbBSPTree bsp; int32_t colidx = 0; SoVRMLColor * color = (SoVRMLColor *)ils->color.getValue(); int n = ils->coordIndex.getNum()-1; for (int i = 0; i < n; i++) { SbVec3f curcol, nextcol; curcol = color->color[colidx]; if (i != n-1) nextcol = color->color[colidx+1]; colidx++; coordIdx.append(bsp.addPoint(curcol)); if (i == n-1 || ils->coordIndex[i+2] == -1) { // Current polyline is done coordIdx.append(coordIdx[coordIdx.getLength()-1]); coordIdx.append(-1); i += 2; } else if (curcol != nextcol) { // Create a new vertex to avoid color interpolation ils->coordIndex.insertSpace(i+1, 1); ils->coordIndex.set1Value(i+1, ils->coordIndex[i+2]); coordIdx.append(bsp.addPoint(curcol)); i++; n++; } } ils->color = thisp->get_or_create_color((const SbColor *)bsp.getPointsArrayPtr(), bsp.numPoints()); ils->colorIndex.setValues(0, coordIdx.getLength(), coordIdx.getArrayPtr()); ils->colorPerVertex = TRUE; } THISP(closure)->insert_shape(action, ils); // it's important to pop state _after_ inserting the shape to get // the correct material from SoVertexProperty nodes. if (vp) { action->getState()->pop(); } return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::sopointset_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; const SoPointSet * oldps = (const SoPointSet*) node; SoVRMLPointSet * ps = NEW_NODE(SoVRMLPointSet, node); SoGroup * tail = thisp->get_current_tail(); // If there is a VertexProperty node set we need to put it on the state stack SoNode *vpnode = oldps->vertexProperty.getValue(); SoVertexProperty *vp = (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ? (SoVertexProperty *)vpnode : NULL; if (vp) { action->getState()->push(); vp->callback(action); } const SoCoordinateElement * coordElem = SoCoordinateElement::getInstance(action->getState()); int numpts = oldps->numPoints.getValue(); // if numPts == -1, use all coordinates on the stack if (numpts < 0 || numpts > coordElem->getNum()) numpts = coordElem->getNum(); if (numpts) { if (coordElem->getArrayPtr3() != NULL) { ps->coord = thisp->get_or_create_coordinate(coordElem->getArrayPtr3(), numpts); } else { ps->coord = thisp->get_or_create_coordinate(coordElem->getArrayPtr4(), numpts); } } if (action->getMaterialBinding() != SoMaterialBinding::OVERALL) { const SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->getNumDiffuse() >= numpts) { if (colorElem->isPacked()) { ps->color = thisp->get_or_create_color(colorElem->getPackedPointer(), numpts); } else { ps->color = thisp->get_or_create_color(colorElem->getDiffusePointer(), numpts); } } } THISP(closure)->insert_shape(action, ps); // it's important to pop state _after_ inserting the shape to get // the correct material from SoVertexProperty nodes. if (vp) { action->getState()->pop(); } return SoCallbackAction::PRUNE; } SoCallbackAction::Response SoToVRML2ActionP::sosphere_cb(void * closure, SoCallbackAction * action, const SoNode * node) { if (action->getDrawStyle() != SoDrawStyle::FILLED) { return SoToVRML2ActionP::sotoifs_cb(closure, action, node); } SoVRMLSphere * sphere = NEW_NODE(SoVRMLSphere, node); const SoSphere * oldsphere = (const SoSphere*) node; if (oldsphere->radius != sphere->radius) sphere->radius = oldsphere->radius.getValue(); THISP(closure)->insert_shape(action, sphere); return SoCallbackAction::PRUNE; } // Property nodes SoCallbackAction::Response SoToVRML2ActionP::soinfo_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoInfo * oldinfo = (const SoInfo*) node; SoVRMLWorldInfo * info = NEW_NODE(SoVRMLWorldInfo, node); info->title = oldinfo->string; THISP(closure)->get_current_tail()->addChild(info); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::somattrans_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoMatrixTransform * oldt = (const SoMatrixTransform*) node; SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); SbVec3f translation, scaleFactor; SbRotation rotation, scaleOrientation; oldt->matrix.getValue().getTransform(translation, rotation, scaleFactor, scaleOrientation); newt->translation = translation.getValue(); newt->rotation = rotation.getValue(); newt->scale = scaleFactor.getValue(); newt->scaleOrientation = scaleOrientation.getValue(); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::sorotation_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoRotation * oldt = (const SoRotation*) node; SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); newt->rotation = oldt->rotation.getValue(); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::sorotationxyz_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoRotationXYZ * oldt = (const SoRotationXYZ*) node; SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); newt->rotation = oldt->getRotation(); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::soscale_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoScale * oldt = (const SoScale*) node; SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); newt->scale = oldt->scaleFactor.getValue(); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::sotransform_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoTransform * oldt = (const SoTransform*) node; SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); newt->translation = oldt->translation.getValue(); newt->rotation = oldt->rotation.getValue(); newt->scale = oldt->scaleFactor.getValue(); newt->scaleOrientation = oldt->scaleOrientation.getValue(); newt->center = oldt->center.getValue(); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::sotranslation_cb(void * closure, SoCallbackAction * action, const SoNode * node) { const SoTranslation * oldt = (const SoTranslation*) node; SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); newt->translation = oldt->translation.getValue(); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::sounits_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoVRMLTransform * newt = NEW_NODE(SoVRMLTransform, node); // apply an SoGetMatrixAction to the node to find the scale factor SbViewportRegion dummy(100,100); SoGetMatrixAction gma(dummy); gma.apply((SoNode*)node); const SbMatrix & m = gma.getMatrix(); // we know that the SoUnits node applies an uniform scale, so just // read the value in the first matrix column/row to find the scale // factor. newt->scale = SbVec3f(m[0][0], m[0][0], m[0][0]); THISP(closure)->get_current_tail()->addChild(newt); THISP(closure)->vrml2path->append(newt); return SoCallbackAction::CONTINUE; } // Other nodes SoCallbackAction::Response SoToVRML2ActionP::sopercam_cb(void * closure, SoCallbackAction * action, const SoNode * node) { return unsupported_cb(closure, action, node); } SoCallbackAction::Response SoToVRML2ActionP::sodirlight_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoVRMLDirectionalLight * dl = NEW_NODE(SoVRMLDirectionalLight, node); const SoDirectionalLight * olddl = (const SoDirectionalLight *) node; dl->direction = olddl->direction.getValue(); dl->on = olddl->on.getValue(); dl->intensity = olddl->intensity.getValue(); dl->color = olddl->color.getValue(); // FIXME: SoDirectionalLight seems to not support this? 20020805 kristian. //dl->ambientIntensity = ambient.getValue()[0] / diffuse.getValue()[0]; THISP(closure)->get_current_tail()->addChild(dl); return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoToVRML2ActionP::sowwwinl_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoVRMLInline * inl = NEW_NODE(SoVRMLInline, node); const SoWWWInline * oldinl = (const SoWWWInline *) node; inl->url = oldinl->name.getValue(); inl->bboxCenter = oldinl->bboxCenter.getValue(); inl->bboxSize = oldinl->bboxSize.getValue(); THISP(closure)->get_current_tail()->addChild(inl); return SoCallbackAction::CONTINUE; } // Convert nodes to ifs SoCallbackAction::Response SoToVRML2ActionP::sotoifs_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; SoGroup * tail = thisp->get_current_tail(); thisp->didpush = FALSE; // push state to handle SoVertexProperty node if (node->isOfType(SoVertexShape::getClassTypeId())) { SoNode *vpnode = ((SoVertexShape *) node)->vertexProperty.getValue(); SoVertexProperty *vp = (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ? (SoVertexProperty *)vpnode : NULL; if (vp) { action->getState()->push(); vp->callback(action); thisp->didpush = TRUE; } } thisp->bsptree = new SbBSPTree; thisp->bsptreenormal = new SbBSPTree; thisp->coordidx = new SbList ; thisp->normalidx = new SbList ; if (action->getMaterialBinding() != SoMaterialBinding::OVERALL) { const SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->getNumDiffuse() > 1) { thisp->coloridx = new SbList ; } } thisp->recentTex2 = (SoTexture2 *) thisp->search_for_recent_node(action, SoTexture2::getClassTypeId()); if (thisp->recentTex2) { thisp->bsptreetex = new SbBSPTree; thisp->texidx = new SbList ; } thisp->do_post_primitives = TRUE; return SoCallbackAction::CONTINUE; } void SoToVRML2ActionP::triangle_cb(void * closure, SoCallbackAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2, const SoPrimitiveVertex * v3) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; assert(thisp->bsptree); assert(thisp->bsptreenormal); SoPrimitiveVertex const * const arr[3] = {v1, v2, v3}; for (int i = 0; i < 3; i++) { const SoPrimitiveVertex * v = arr[i]; thisp->coordidx->append(thisp->bsptree->addPoint(v->getPoint())); thisp->normalidx->append(thisp->bsptreenormal->addPoint(v->getNormal())); if (thisp->texidx) { assert(thisp->bsptreetex); const SbVec4f & tc = v->getTextureCoords(); thisp->texidx->append(thisp->bsptreetex->addPoint(SbVec3f(tc[0], tc[1], 0.0f))); } if (thisp->coloridx) thisp->coloridx->append(v->getMaterialIndex()); } thisp->coordidx->append(-1); thisp->normalidx->append(-1); if (thisp->texidx) thisp->texidx->append(-1); if (thisp->coloridx) thisp->coloridx->append(-1); } SoCallbackAction::Response SoToVRML2ActionP::post_primitives_cb(void * closure, SoCallbackAction * action, const SoNode * node) { SoToVRML2ActionP * thisp = (SoToVRML2ActionP*) closure; if (!thisp->do_post_primitives) return SoCallbackAction::CONTINUE; thisp->do_post_primitives = FALSE; SoVRMLGeometry * is; if (action->getDrawStyle() == SoDrawStyle::POINTS) { SoVRMLPointSet * ps = NEW_NODE(SoVRMLPointSet, node); is = ps; ps->coord = thisp->get_or_create_coordinate(thisp->bsptree->getPointsArrayPtr(), thisp->bsptree->numPoints()); if (thisp->coloridx) { // Copy the colors from the state SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->getNumDiffuse() == thisp->bsptree->numPoints()) { if (colorElem->isPacked()) { ps->color = thisp->get_or_create_color(colorElem->getPackedPointer(), colorElem->getNumDiffuse()); } else { ps->color = thisp->get_or_create_color(colorElem->getDiffusePointer(), colorElem->getNumDiffuse()); } } } } else if (action->getDrawStyle() == SoDrawStyle::LINES) { SoVRMLIndexedLineSet * ils = NEW_NODE(SoVRMLIndexedLineSet, node); is = ils; ils->coord = thisp->get_or_create_coordinate(thisp->bsptree->getPointsArrayPtr(), thisp->bsptree->numPoints()); if (thisp->coloridx) { // Copy the colors from the state const SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->isPacked()) { ils->color = thisp->get_or_create_color(colorElem->getPackedPointer(), colorElem->getNumDiffuse()); } else { ils->color = thisp->get_or_create_color(colorElem->getDiffusePointer(), colorElem->getNumDiffuse()); } // Index ils->colorIndex.setValues(0, thisp->coloridx->getLength(), thisp->coloridx->getArrayPtr()); } int n = thisp->coordidx->getLength(); const int32_t * a = thisp->coordidx->getArrayPtr(); SbList l; int32_t p = a[0]; for (int i = 0; i < n; i++) { if (a[i] == -1) { l.append(p); if (i < n-1) p = a[i+1]; } l.append(a[i]); } ils->coordIndex.setValues(0, l.getLength(), l.getArrayPtr()); } else { SoVRMLIndexedFaceSet * ifs = NEW_NODE(SoVRMLIndexedFaceSet, node); is = ifs; // Set the values from the current ShapeHints ifs->creaseAngle = action->getCreaseAngle(); if (node->isOfType(SoVertexShape::getClassTypeId())) { ifs->ccw = action->getVertexOrdering() != SoShapeHints::CLOCKWISE; } else { ifs->ccw = TRUE; } ifs->solid = SoShapeHintsElement::getShapeType(action->getState()) == SoShapeHintsElement::SOLID; ifs->convex = action->getFaceType() == SoShapeHints::CONVEX; ifs->coord = thisp->get_or_create_coordinate(thisp->bsptree->getPointsArrayPtr(), thisp->bsptree->numPoints()); ifs->normal = thisp->get_or_create_normal(thisp->bsptreenormal->getPointsArrayPtr(), thisp->bsptreenormal->numPoints()); if (thisp->coloridx) { // Copy the colors from the state const SoLazyElement * colorElem = SoLazyElement::getInstance(action->getState()); if (colorElem->isPacked()) { ifs->color = thisp->get_or_create_color(colorElem->getPackedPointer(), colorElem->getNumDiffuse()); } else { ifs->color = thisp->get_or_create_color(colorElem->getDiffusePointer(), colorElem->getNumDiffuse()); } // Index ifs->colorIndex.setValues(0, thisp->coloridx->getLength(), thisp->coloridx->getArrayPtr()); } if (thisp->texidx) { // Copy texture coordinates SoVRMLTextureCoordinate * tex = new SoVRMLTextureCoordinate; int n = thisp->bsptreetex->numPoints(); tex->point.setNum(n); SbVec2f * ptr = tex->point.startEditing(); for (int i = 0; i < n; i++) { SbVec3f p = thisp->bsptreetex->getPoint(i); ptr[i] = SbVec2f(p[0], p[1]); } tex->point.finishEditing(); ifs->texCoord = tex; // Index ifs->texCoordIndex.setValues(0, thisp->texidx->getLength(), thisp->texidx->getArrayPtr()); } ifs->coordIndex.setValues(0, thisp->coordidx->getLength(), thisp->coordidx->getArrayPtr()); ifs->normalIndex.setValues(0, thisp->normalidx->getLength(), thisp->normalidx->getArrayPtr()); } delete thisp->bsptree; thisp->bsptree = NULL; delete thisp->bsptreetex; thisp->bsptreetex = NULL; delete thisp->bsptreenormal; thisp->bsptreenormal = NULL; delete thisp->coordidx; thisp->coordidx = NULL; delete thisp->normalidx; thisp->normalidx = NULL; delete thisp->texidx; thisp->texidx = NULL; delete thisp->coloridx; thisp->coloridx = NULL; thisp->insert_shape(action, is); if (thisp->didpush) { action->getState()->pop(); } return SoCallbackAction::CONTINUE; } #undef NEW_NODE #undef DEFAULT_VIEWPORT_WIDTH #undef DEFAULT_VIEWPORT_HEIGHT #endif // HAVE_VRML97