/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ /* * Copyright (C) 1990-95 Silicon Graphics, Inc. * * _______________________________________________________________________ ______________ S I L I C O N G R A P H I C S I N C . ____________ | | $Revision: 1.9 $ | | Classes : SoSceneViewer | | Author(s) : Thad Beier, David Mott, Alain Dumesny, Paul Isaacs, | Rikk Carey, Dave Immel | ______________ S I L I C O N G R A P H I C S I N C . ____________ _______________________________________________________________________ */ #include // for system() and getenv() #include // for access() #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 #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 #include #include #include #include #include #include #include #include #include #include #include #include #include "SoSceneViewer.h" #include "SoSceneMenu.h" #include "SvManipList.h" #include "../../samples/widgets/MyColorEditor.h" #include "../../samples/widgets/MyFileRead.h" #include "../../samples/widgets/MyDropSite.h" #include "../../samples/common/InventorLogo.h" #include #ifdef DEBUG #include #endif // // Macros and constants // // toggle button macros #define TOGGLE_ON(BUTTON) \ XmToggleButtonSetState((Widget) BUTTON, TRUE, FALSE) #define TOGGLE_OFF(BUTTON) \ XmToggleButtonSetState((Widget) BUTTON, FALSE, FALSE) #define FOG_FUDGE 1.6 #define SV_NUM_LIGHTS 6 #define SWITCH_LIGHT_OFF(SWITCH) (SWITCH)->whichChild.setValue(SO_SWITCH_NONE) #define SWITCH_LIGHT_ON(SWITCH) (SWITCH)->whichChild.setValue(SO_SWITCH_ALL) #define IS_LIGHT_ON(SWITCH) ((SWITCH)->whichChild.getValue() == SO_SWITCH_ALL) #define SV_ENV_LABEL "SoSceneViewer Environment v3.0" #define _ATOM_(DISPLAY,NAME) (XmInternAtom(DISPLAY, NAME, False)) #define SCREEN(w) XScreenNumberOfScreen(XtScreen(w)) // // Structs // struct SoSceneViewerData { int id; SoSceneViewer *classPt; Widget widget; }; class SvLightData { public: // Constructor inits everything to NULL SvLightData(); SoSceneViewer *classPt; SoSwitch *lightSwitch; SoTranslation *translation; // for placing a directional light manip SoScale *scale; SoLight *light; SoScale *scaleInverse; SoTranslation *translationInverse; SoType type; char *name; MyColorEditor *colorEditor; SbBool isManip(); SbBool shouldBeManip; // Used to remember what it was when // they all get turned off for writing, // printing, etc. Widget cascadeWidget; Widget submenuWidget; Widget onOffWidget; Widget iconWidget; Widget editColorWidget; Widget removeWidget; }; SvLightData::SvLightData() { classPt = NULL; lightSwitch = NULL; translation = NULL; scale = NULL; light = NULL; scaleInverse = NULL; translationInverse = NULL; name = NULL; colorEditor = NULL; shouldBeManip = FALSE; cascadeWidget = NULL; submenuWidget = NULL; onOffWidget = NULL; iconWidget = NULL; editColorWidget = NULL; removeWidget = NULL; } SbBool SvLightData::isManip() { if (light == NULL) return FALSE; if (light->isOfType( SoDirectionalLightManip::getClassTypeId())) return TRUE; else if (light->isOfType( SoPointLightManip::getClassTypeId()) ) return TRUE; else if (light->isOfType( SoSpotLightManip::getClassTypeId()) ) return TRUE; return FALSE; } //////////////////////////////////////////////////////////////////////// // // Set the Inventor logo on the screen. // static void logoCB(void *, SoAction *action) { if (action->isOfType(SoGLRenderAction::getClassTypeId())) { glViewport(0, 0, 80, 80); } } static void setOverlayLogo(SoXtRenderArea *ra) { static SoSeparator *logo = NULL; if (logo == NULL) { SoInput in; in.setBuffer((void *)ivLogo, ivLogoSize); logo = SoDB::readAll(&in); logo->ref(); // Add a callback node which will set the viewport SoCallback *cb = new SoCallback; cb->setCallback(logoCB); logo->insertChild(cb, 0); } SbColor col(1, 1, 1); ra->setOverlayColorMap(1, 1, &col); ra->setOverlaySceneGraph(logo); } // //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // Public constructor - build the widget right now // SoSceneViewer::SoSceneViewer( Widget parent, const char *name, SbBool buildInsideParent, SoSelection *inputGraph, const char *envFile) : SoXtComponent( parent, name, buildInsideParent) // //////////////////////////////////////////////////////////////////////// { // In this case, render area is what the app wants, so buildNow = TRUE constructorCommon(inputGraph, envFile, TRUE); } //////////////////////////////////////////////////////////////////////// // // SoEXTENDER constructor - the subclass tells us whether to build or not // SoSceneViewer::SoSceneViewer( Widget parent, const char *name, SbBool buildInsideParent, SoSelection *inputGraph, const char *envFile, SbBool buildNow) : SoXtComponent( parent, name, buildInsideParent) // //////////////////////////////////////////////////////////////////////// { // In this case, render area may be what the app wants, // or it may want a subclass of render area. Pass along buildNow // as it was passed to us. constructorCommon(inputGraph, envFile, buildNow); } //////////////////////////////////////////////////////////////////////// // // Called by the constructors // // private // void SoSceneViewer::constructorCommon( SoSelection *inputGraph, const char *envFile, SbBool buildNow) // //////////////////////////////////////////////////////////////////////// { int i; // getting resources. XtGetSubresources( getParentWidget(), &fileData[0], "intl", "Intl", intl_file_resources, intl_num_file_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &editData[0], "intl", "Intl", intl_edit_resources, intl_num_edit_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &manipData[0], "intl", "Intl", intl_manip_resources, intl_num_manip_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &selData[0], "intl", "Intl", intl_sel_resources, intl_num_sel_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &viewData[0], "intl", "Intl", intl_view_resources, intl_num_view_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &editorData[0], "intl", "Intl", intl_editor_resources, intl_num_editor_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &lightData[0], "intl", "Intl", intl_light_resources, intl_num_light_resources, NULL, 0 ); XtGetSubresources( getParentWidget(), &pulldownData[0], "intl", "Intl", intl_pulldown_resources, intl_num_pulldown_resources, NULL, 0 ); setClassName("SoSceneViewer"); setSize( SbVec2s(520, 510) ); // selection is the users scene graph. selection = inputGraph; currentViewer = NULL; createLightsCameraEnvironment(); // the scene viewer supplies its own camera and lights. // in fact, we remove any cameras that might be in the users graph. // NOTE: since the camera may be switched by the viewer (ortho/perspective toggle) // make sure to get the camera from the viewer (and not cache the camera). sceneGraph = new SoSeparator(); sceneGraph->ref(); // must ref it sceneGraph->addChild(lightsCameraEnvironment); sceneGraph->addChild(selection); // Create the dictionary that keeps track of original camera // settings in scenekits sceneKitCamNumDict = new SbDict(5);; #ifndef EXPLORER SoCamera *camFromScene = getLastSceneKitCamera(selection); switchOffSceneKitCameras(selection); removeCameras(selection); #endif // // Widget and menu variables // mgrWidget = NULL; showMenuFlag = TRUE; menuWidget = NULL; menuItems = new SoSceneViewerData[SV_MENU_NUM]; for (i=0; ifogType.setValue( SoEnvironment::NONE ); antialiasingFlag = FALSE; backgroundColorEditor = NULL; // // Selection // These callbacks are used to update the SceneViewer state after // the current selection changes (e.g. attach/detach editors and manips). // selection->addSelectionCallback(SoSceneViewer::selectionCallback, this); selection->addDeselectionCallback(SoSceneViewer::deselectionCallback, this); selection->setPickFilterCallback(SoSceneViewer::pickFilterCB, this); highlightRA = new SoBoxHighlightRenderAction; // // Editors // ignoreCallback = FALSE; materialEditor = NULL; colorEditor = NULL; transformSliderSet = NULL; // // Manips // curManip = SV_NONE; highlightRA->setVisible(TRUE); // highlight visible when no manip curManipReplaces = TRUE; maniplist = new SvManipList; #ifdef EXPLORER // // User callback // userModeCB = NULL; userModedata = NULL; userModeFlag = FALSE; #endif /* EXPLORER */ // // Lights // ambientColorEditor = NULL; headlightData = new SvLightData; headlightData->classPt = this; headlightData->name = Light.headlight; headlightData->type = SoDirectionalLight::getClassTypeId(); headlightData->colorEditor = NULL; headlightData->shouldBeManip = FALSE; headlightEditor = NULL; calculatedLightManipSize = FALSE; // Build the widget tree, and let SoXtComponent know about our base widget. if (buildNow) { Widget w = buildWidget(getParentWidget()); setBaseWidget(w); } // do this after everything else has been set up (viewer needs to exists // because the camera is gotten from the viewer (not cached here)) if (envFile != NULL) readEnvFile(envFile); // If scene had sceneKit with a camera, copy its values into our camera. if (camFromScene) setCameraFields( camFromScene ); } //////////////////////////////////////////////////////////////////////// // // Description: // Destructor. // // Use: public SoSceneViewer::~SoSceneViewer() // //////////////////////////////////////////////////////////////////////// { // Delete the dictionary that keeps track of original camera // settings in scenekits delete sceneKitCamNumDict; // detach and delete the manips detachManipFromAll(); delete maniplist; // detach and delete the viewers currentViewer->setSceneGraph(NULL); delete (SoXtExaminerViewer *) viewerList[SV_VWR_EXAMINER]; delete (SoXtFlyViewer *) viewerList[SV_VWR_FLY]; delete (SoXtWalkViewer *) viewerList[SV_VWR_WALK]; delete (SoXtPlaneViewer *) viewerList[SV_VWR_PLANE]; // delete menu items data delete [ /*SV_MENU_NUM*/ ] menuItems; delete headlightData; delete headlightEditor; delete printDialog; // Editor components delete materialEditor; delete colorEditor; delete transformSliderSet; delete ambientColorEditor; delete backgroundColorEditor; sceneGraph->unref(); } //////////////////////////////////////////////////////////////////////// // // Description: // New data is going to be coming into the viewer. Time to disconnect all // manipulators and picking, and wait for new information. Might as well go // into a viewing mode as well, this gets rid of the manipulators, and puts // the user in control of viewing when new data shows up. // // Use: public void SoSceneViewer::newData() // //////////////////////////////////////////////////////////////////////// { selection->deselectAll(); } #ifdef EXPLORER //////////////////////////////////////////////////////////////////////// // // Description: // This sets the user mode callack routine // // Use: public void SoSceneViewer::setUserModeEventCallback(SoXtRenderAreaEventCB *fcn) // //////////////////////////////////////////////////////////////////////// { userModeCB = fcn; userModedata = currentViewer->getSceneGraph(); if (userModeFlag) currentViewer->setEventCallback(userModeCB, userModedata); } #endif /* EXPLORER */ //////////////////////////////////////////////////////////////////////// // // Description: // switches from the current viewer to the given viewer. The new // viewer will automatically be allocated if needed, and set to have // the same settings the current viewer has (drawing style, buffer // type, etc..). // // Use: private void SoSceneViewer::switchToViewer(SvEViewer newViewer) // //////////////////////////////////////////////////////////////////////// { if (whichViewer == newViewer) return; // remove the sensor + scene graph from the old viewer (to prevent // an unecessary redraw of the old viewer) currentViewer->setAutoRedraw(FALSE); currentViewer->setSceneGraph(NULL); // allocate the viewer if needed and set the window title. // all viewers share the same highlight render action. switch (newViewer) { case SV_VWR_EXAMINER: // examiner vwr is already created in build() setTitle("SceneViewer (Examiner)"); break; case SV_VWR_FLY: if ( viewerList[newViewer] == NULL ) { viewerList[newViewer] = new SoXtFlyViewer(mgrWidget); viewerList[newViewer]->setGLRenderAction(highlightRA); viewerList[newViewer]->redrawOnSelectionChange(selection); } setTitle("SceneViewer (Fly)"); break; case SV_VWR_WALK: if ( viewerList[newViewer] == NULL ) { viewerList[newViewer] = new SoXtWalkViewer(mgrWidget); viewerList[newViewer]->setGLRenderAction(highlightRA); viewerList[newViewer]->redrawOnSelectionChange(selection); } setTitle("SceneViewer (Walk)"); break; case SV_VWR_PLANE: if ( viewerList[newViewer] == NULL ) { viewerList[newViewer] = new SoXtPlaneViewer(mgrWidget); viewerList[newViewer]->setGLRenderAction(highlightRA); viewerList[newViewer]->redrawOnSelectionChange(selection); } setTitle("SceneViewer (Plane)"); break; } SoXtFullViewer *newVwr = viewerList[newViewer]; // re-init the render action (since it is shared between viewers) now that // we are changing windows + set the new scene graph newVwr->setAutoRedraw(TRUE); newVwr->setSceneGraph(sceneGraph); newVwr->getGLRenderAction()->invalidateState(); // // make sure the new viewer has all the same settings as // the current viewer. // // XtRenderArea methods //???can't each viewer have it's own background color? especially //???if we nuke the background color editor. The user might set the //???colors to be different in the app-defaults file. //???newVwr->setBackgroundColor( currentViewer->getBackgroundColor() ); environment->fogColor.setValue( newVwr->getBackgroundColor() ); newVwr->setClearBeforeRender( currentViewer->isClearBeforeRender() ); // XtViewer methods newVwr->setHeadlight( currentViewer->isHeadlight() ); newVwr->setDrawStyle( SoXtViewer::STILL, currentViewer->getDrawStyle(SoXtViewer::STILL) ); newVwr->setDrawStyle( SoXtViewer::INTERACTIVE, currentViewer->getDrawStyle(SoXtViewer::INTERACTIVE) ); newVwr->setBufferingType( currentViewer->getBufferingType() ); newVwr->setViewing( currentViewer->isViewing() ); newVwr->setAutoClipping( currentViewer->isAutoClipping() ); newVwr->setSeekTime( currentViewer->getSeekTime() ); // XtFullViewer methods newVwr->setDecoration( currentViewer->isDecoration() ); // The tabBoxManip adds a finish callback to the viewer. // We need to remove from the old and add to the new. for (int m = 0; m < maniplist->getLength(); m++ ) { SoTransformManip *manip = maniplist->getManip(m); if ( manip->isOfType( SoTabBoxManip::getClassTypeId() ) ) { currentViewer->removeFinishCallback( &SoSceneViewer::adjustScaleTabSizeCB, manip->getDragger() ); newVwr->addFinishCallback( &SoSceneViewer::adjustScaleTabSizeCB, manip->getDragger() ); } } #ifdef EXPLORER if (userModeFlag) newVwr->setEventCallback(userModeCB, userModedata); else newVwr->setEventCallback(NULL, NULL); #endif // build and layout the new viewer buildAndLayoutViewer(newVwr); // finally switch to the new viewer by showing the new viewer, // and hidding the old viewer (hide is done last to reduce flicker). newVwr->show(); currentViewer->hide(); whichViewer = newViewer; currentViewer = newVwr; setOverlayLogo(currentViewer); } //////////////////////////////////////////////////////////////////////// // // Description: // Move up the picked path to the parent group. // // Use: public void SoSceneViewer::pickParent() // //////////////////////////////////////////////////////////////////////// { SoFullPath *pickPath; int parentIndex; // We'll pick the parent of the last selection in the list... pickPath = (SoFullPath *) (*selection)[selection->getNumSelected() - 1]; if(pickPath == NULL || pickPath->getLength() < 2) return; // Get actual node that is the current selection: SoNode *tail = pickPath->getTail(); SoNode *kitTail = ((SoNodeKitPath *)pickPath)->getTail(); SoType nkt = SoBaseKit::getClassTypeId(); if ( kitTail->isOfType( nkt ) ) tail = kitTail; else kitTail = NULL; // If kitTail is at top of path, we've already gone as high as we can go. if ( kitTail == pickPath->getHead() ) return; // Get index of parent of selection. if ( kitTail != NULL ) { // Look for first kit above tail. If none, use direct parent of kitTail. SoNode *aboveTail = ((SoNodeKitPath *)pickPath)->getNodeFromTail( 1 ); SbBool aboveIsKit = aboveTail->isOfType( nkt ); for (int i = pickPath->getLength() - 1; i >= 0; i-- ) { if ( aboveIsKit ) { if (pickPath->getNode(i) == aboveTail ) { parentIndex = i; break; } } else if ( pickPath->getNode(i) == kitTail ) { parentIndex = i - 1; break; } } } else { // If tail is not a nodkeit, parentIndex is just parent of tail... parentIndex = pickPath->getLength() - 2; } // cannot select the selection node (make sure we're not) if (pickPath->getNode(parentIndex) == selection) { fprintf(stderr, "No more parents to pick (cannot pick above the selection node)\n"); return; } pickPath->ref(); // need to ref it, because // selection->clear unref's it selection->deselectAll(); pickPath->truncate(parentIndex + 1); // Make path end at parentIndex selection->select(pickPath); // add path back in pickPath->unref(); // now we can unref it, again } //////////////////////////////////////////////////////////////////////// // // Description: // Pick all group nodes and shapes under selection. // // Use: private void SoSceneViewer::pickAll() // //////////////////////////////////////////////////////////////////////// { selection->deselectAll(); #ifdef DEBUG assert(selection != NULL); #endif SoPathList myPaths; // Our callbacks on the selection's 'select' method may add // more children to the selection node (by making a trackball or handlebox) // Therefore, we must first determine the selections by storing // paths to them. // Following this, we call 'select' on each path, in turn. // // Create paths from the selection node to all of it's children // that are groups or shapes. // for (int i = 0; i < selection->getNumChildren(); i++) { SoNode *node = selection->getChild(i); if ((node->isOfType(SoGroup::getClassTypeId()) || node->isOfType(SoShape::getClassTypeId())) ) { SoPath *thisPath = new SoPath(selection); thisPath->append(i); myPaths.append( thisPath ); } } // // Select each path in 'myPaths' // for (int j = 0; j < myPaths.getLength(); j++) selection->select(myPaths[j]); } //////////////////////////////////////////////////////////////////////// // // Description: // This routine first detaches manipulators from all selected objects, // then attaches a manipulator to all selected objects. // // Use: private void SoSceneViewer::replaceAllManips( SvEManipMode manipMode ) // Current manipulator // //////////////////////////////////////////////////////////////////////// { detachManipFromAll(); attachManipToAll( manipMode ); } //////////////////////////////////////////////////////////////////////// // // Description: // This routine attaches a manipulator to all selected objects. // // Use: private void SoSceneViewer::attachManipToAll( SvEManipMode manipMode ) // Current manipulator // //////////////////////////////////////////////////////////////////////// { int i; for ( i = 0; i < selection->getNumSelected(); i++ ) { SoPath *p = (*selection)[i]; attachManip( manipMode, p ); } } //////////////////////////////////////////////////////////////////////// // // Description: // This routine attaches and activates a manipulator. // // Use: private void SoSceneViewer::attachManip( SvEManipMode manipMode, // Current manipulator SoPath *selectedPath ) // Which selection to attach to // //////////////////////////////////////////////////////////////////////// { SoTransformManip *theXfManip; SoPath *xfPath; // // Attach to a manipulator. // if ( manipMode == SV_NONE ) return; xfPath = findTransformForAttach( selectedPath ); xfPath->ref(); theXfManip = NULL; switch( manipMode ) { case SV_TRANSFORMER: theXfManip = new SoTransformerManip; break; case SV_TRACKBALL: theXfManip = new SoTrackballManip; break; case SV_HANDLEBOX: theXfManip = new SoHandleBoxManip; break; case SV_JACK: theXfManip = new SoJackManip; break; case SV_CENTERBALL: theXfManip = new SoCenterballManip; break; case SV_XFBOX: theXfManip = new SoTransformBoxManip; break; case SV_TABBOX: theXfManip = new SoTabBoxManip; break; case SV_NONE: return; } if ( theXfManip ) { SoFullPath *fp = (SoFullPath *) xfPath; #ifdef DEBUG if ( !fp->getTail()->isOfType( SoTransform::getClassTypeId() ) ) { fprintf(stderr,"DBG> Fatal Error: in SoSceneViewer::attachManip\n"); fprintf(stderr," > end of path is not a transform\n"); } #endif SoTransform *oldXf = (SoTransform *)fp->getTail(); oldXf->ref(); theXfManip->ref(); if ( !theXfManip->replaceNode( xfPath ) ) { theXfManip->unref(); #ifdef DEBUG fprintf(stderr,"DBG> Fatal Error: in SoSceneViewer::attachManip\n"); fprintf(stderr," > manip->replaceNode() failed!\n" ); #endif } // If the transformSliderSet is attached to the oldXf, then attach // it to the new manip instead. if ( transformSliderSet && transformSliderSet->isVisible() && transformSliderSet->getNode() == oldXf) transformSliderSet->setNode(theXfManip); // Add manip and paths to the maniplist (maniplist will ref/unref) maniplist->append(selectedPath, theXfManip, xfPath ); theXfManip->unref(); oldXf->unref(); if ( manipMode == SV_TABBOX ) { // Special case! When using a tab box, we want to adjust the // scale tabs upon viewer finish. currentViewer->addFinishCallback( &SoSceneViewer::adjustScaleTabSizeCB,theXfManip->getDragger()); } if ( manipMode == SV_JACK ) { // Special case! For jack manip, we want it so that clicking on the // selected object initiates 2-dimensional translation. Other // parts of the jack manip should use the default resource geometry. // So, we replace the parts that do planar motion. // We need to replace a total of six parts: // 'translator.yzTranslator.translator': // 'translator.xzTranslator.translator': // 'translator.xyTranslator.translator': // 'translator.yzTranslator.translatorActive': // 'translator.xzTranslator.translatorActive': // 'translator.xyTranslator.translatorActive': // In the SoJackDragger, 'translator' is an SoDragPointDragger, // which takes care of all translations in 3 dimensions for jack. // In the SoDragPointDragger there are 3 planar translation parts // (each is an SoTranslate2Dragger) and 3 linear translation parts // (each is an SoTranslate1Dragger). At any given time, dragPoint // displays one of each kind of dragger. We leave the linear // translators as the default geometry (a cylinder along the axis // of motion), but replace the geometry in the planar translators. // Within the SoDragPointDragger, the planar translators are named // 'yzTranslator', 'xzTranslator', and 'xyTranslator'. // Each of these is an SoTranslate2Dragger, which has two parts // for its geometry, 'translator' and 'translatorActive.' Clicking // on the 'translator' is what initiates the 2D translation. // Once begun, the 'translatorActive' part is displayed. We // replace both of these parts with a path to the selected object. // When we call setPartAsPath, we need to prune the path if our // selected geometry lays inside a nodekit. // For example, let's say a dumbBellKit contains 1 bar (a cylinder) // and 2 end (spheres). Since this is the lowest-level kit // containing these 3 shapes, they are considered a single object // by the SceneViewer. // So we need to pass a path to the kit, not the individual piece // that is selected. This way, subsequent clicks on any piece of // the dumbbell will cause 2D translation. // First, is a nodekit on the path? If so, find the last one. SoFullPath *jackP = (SoFullPath *) selectedPath; SoType bkType = SoBaseKit::getClassTypeId(); int lastKitInd = -1; for (int i = jackP->getLength() - 1; i >= 0; i--) { if (jackP->getNode(i)->isOfType(bkType)) { lastKitInd = i; break; } } // If there's a lastKitInd, make jackP be a copy of // selectedPath, but only up to lastKitInd. if ( lastKitInd != -1) jackP = (SoFullPath *) selectedPath->copy(0,lastKitInd + 1); // Get the dragger from the manip (the manip contains the dragger, // and the dragger has the parts). SoDragger *d = theXfManip->getDragger(); // Use jackP to set the translator parts, then discard (unref) it: jackP->ref(); d->setPartAsPath("translator.yzTranslator.translator", jackP ); d->setPartAsPath("translator.xzTranslator.translator", jackP ); d->setPartAsPath("translator.xyTranslator.translator", jackP ); d->setPartAsPath( "translator.yzTranslator.translatorActive", jackP); d->setPartAsPath( "translator.xzTranslator.translatorActive", jackP); d->setPartAsPath( "translator.xyTranslator.translatorActive", jackP); jackP->unref(); } } xfPath->unref(); } //////////////////////////////////////////////////////////////////////// // // Description: // This routine detaches the manipulators from all selected objects. // // Use: private void SoSceneViewer::detachManipFromAll() // //////////////////////////////////////////////////////////////////////// { // // Loop from the end of the list to the start. // for (int i = selection->getNumSelected() - 1; i >= 0 ; i-- ) { SoPath *p = (SoPath *) (*selection)[i]; detachManip( p ); } } //////////////////////////////////////////////////////////////////////// // // Description: // This routine detaches a manipulator. // // Use: private void SoSceneViewer::detachManip( SoPath *p ) // Selection object that is being removed // //////////////////////////////////////////////////////////////////////// { // // Detach manip and remove from scene graph. // int which = maniplist->find(p); // See if this path is registered in the manip list. if (which != -1) { // remove from scene graph SoTransformManip *manip = maniplist->getManip(which); if ( manip->isOfType(SoTabBoxManip::getClassTypeId() )) { // Special case! When using a tab box, we want to adjust the // scale tabs upon viewer finish. currentViewer->removeFinishCallback( &SoSceneViewer::adjustScaleTabSizeCB, manip->getDragger() ); } SoPath *xfPath = maniplist->getXfPath(which); SoTransform *newXf = new SoTransform; newXf->ref(); manip->ref(); // replace the manip manip->replaceManip( xfPath, newXf ); // If the transformSliderSet is attached to the manip, then attach // it to the new node instead. if ( transformSliderSet && transformSliderSet->isVisible() && transformSliderSet->getNode() == manip) transformSliderSet->setNode(newXf); manip->unref(); newXf->unref(); // remove from maniplist maniplist->remove(which); } } //////////////////////////////////////////////////////////////////////// // // Description: // Added as a finish callback to the current viewer. It makes sure // the scale tab size gets changed when a viewer gesture is // completed. // // Use: public void SoSceneViewer::adjustScaleTabSizeCB( void *userData, SoXtViewer *) // //////////////////////////////////////////////////////////////////////// { SoTabBoxDragger *dragger = (SoTabBoxDragger *) userData; dragger->adjustScaleTabSize(); } //////////////////////////////////////////////////////////////////////// // // Description: // See the selection from the camera // // Use: public void SoSceneViewer::viewSelection() // //////////////////////////////////////////////////////////////////////// { if (selection->getNumSelected() == 0) { viewAll(); return; } SoPath *path = (*selection)[0]; if(path != NULL) { getCamera()->viewAll(path, currentViewer->getViewportRegion()); } else { viewAll(); return; } } //////////////////////////////////////////////////////////////////////// // // Description: // Create a color editor for the currently selected object. // Attachment code copied from SoXformManip.c++ // // Use: private void SoSceneViewer::createColorEditor() // //////////////////////////////////////////////////////////////////////// { if (colorEditor == NULL) { colorEditor = new MyColorEditor; colorEditor->setWYSIWYG(TRUE); colorEditor->setTitle("Diffuse Color"); } SoMaterial *editMaterial = findMaterialForAttach( NULL ); colorEditor->attach(&(editMaterial->diffuseColor), 0, editMaterial); colorEditor->show(); } //////////////////////////////////////////////////////////////////////// // // Description: // Find the appropriate material node in the scene graph to attach a material // editor to. // // Two possible cases: // [1] The path-tail is NOT a group. We search the siblings of the path // tail (including the tail itself) from right to left for a node // that is affected by materials (shapes or groups). // We stop the search if we come to a material node to the left of the // pathTail. If we find a node that IS affected by material, we will // insert a material node just before the path-tail. This is // because the editor should not affect nodes that appear // before attachPath in the scene graph. // [2] The path-tail IS a group. We search the children from left to // right for material nodes. // We stop the search if we come to a material node. // If we find a node that is affected by materials, we will insert a // material just before this node. This is because the editor for a // group should affect ALL nodes within that group. // // NOTE: For the purposes of this routine, we consider SoSwitch as different // from other types of group. This is because we don't want to put // the new node underneath the switch, but next to it. // // Use: private // SoMaterial * SoSceneViewer::findMaterialForAttach( const SoPath *target ) // path to start search from // //////////////////////////////////////////////////////////////////////// { int pathLength; SoPath *selectionPath; SoMaterial *editMtl; SbBool madeNewMtl = FALSE; // did we create a new material // node within this method? if ( ( selectionPath = (SoPath *) target ) == NULL ) { // // If no selection path is specified, then use the LAST path in the // current selection list. // selectionPath = (*selection)[selection->getNumSelected() - 1]; // last guy } pathLength = selectionPath->getLength(); if ( pathLength <= 0 ) { fprintf( stderr, "No objects currently selected...\n" ); return NULL; } #ifdef DEBUG if ( pathLength < 2 ) { fprintf( stderr, "Picked object has no parent...\n" ); return NULL; } #endif // find 'group' and try to find 'editMtl' SoGroup *group; SoNode *node; int index, i; SbBool ignoreNodekit = FALSE; editMtl = NULL; if ( selectionPath->getTail()->isOfType( SoBaseKit::getClassTypeId() )) { // Nodekits have their own built in policy for creating new material // nodes. Allow them to contruct and return it. // Get the last nodekit in the path: SoBaseKit *kit = (SoBaseKit *) ((SoNodeKitPath *)selectionPath)->getTail(); // SO_CHECK_PART returns NULL if the part doesn't exist yet... editMtl = SO_GET_PART( kit, "material", SoMaterial ); if ( editMtl == NULL ) { // This nodekit does not have a material part. // Ignore the fact that this is a nodekit. ignoreNodekit = TRUE; } } SbBool isTailGroup = selectionPath->getTail()->isOfType( SoGroup::getClassTypeId()) && (!selectionPath->getTail()->isOfType( SoSwitch::getClassTypeId())); if ((editMtl == NULL) && ( !isTailGroup )) { // // CASE 1: The path-tail is not a group. // 'group' becomes the second to last node in the path. // We search the path tail and its siblings from right to left for a // mtl node. // We stop the search if we come to a shape node or a group node // to the left of the pathTail. If we find a shape or group, we // will insert a mtl just before the path-tail. This is // because the manipulator should not affect objects that appear // before selectionPath in the scene graph. // group = (SoGroup *) selectionPath->getNode(pathLength - 2); index = group->findChild( selectionPath->getTail() ); for (i = index; (i >= 0) && (editMtl == NULL); i--){ node = group->getChild(i); if (node->isOfType(SoMaterial::getClassTypeId())) // found SoMaterial editMtl = (SoMaterial *) node; else if ( i != index ) { if ( isAffectedByMaterial( node ) ) break; } } if ( editMtl == NULL ) { editMtl = new SoMaterial; group->insertChild( editMtl, index ); madeNewMtl = TRUE; } } else if (editMtl == NULL) { // CASE 2: The path-tail is a group. // 'group' becomes the path tail // We search the children from left to right for mtl nodes. // We stop the search if we come to a shape node or a group node. // If we find a shape or group, we will insert a mtl just // before this shape or group. This is because the editor // for a group should affect ALL objects within that group. // group = (SoGroup *) selectionPath->getTail(); for (i = 0; (i < group->getNumChildren()) && (editMtl == NULL); i++ ) { node = group->getChild(i); if (node->isOfType(SoMaterial::getClassTypeId())) editMtl = (SoMaterial *) node; else if ( isAffectedByMaterial( node ) ) break; } if ( editMtl == NULL ) { editMtl = new SoMaterial; group->insertChild( editMtl, i ); madeNewMtl = TRUE; } } // If we just created the material node here, then set the ignore // flags for all fields in the node. This will cause the fields // to be inherited from their ancestors. The material editor will // undo these flags whenever it changes the value of a field if ( madeNewMtl == TRUE ) { editMtl->ambientColor.setIgnored( TRUE ); editMtl->diffuseColor.setIgnored( TRUE ); editMtl->specularColor.setIgnored( TRUE ); editMtl->emissiveColor.setIgnored( TRUE ); editMtl->shininess.setIgnored( TRUE ); editMtl->transparency.setIgnored( TRUE ); } // If any of the fields is ignored, then fill the value with the value // inherited from the rest of the scene graph if ( editMtl->ambientColor.isIgnored() || editMtl->diffuseColor.isIgnored() || editMtl->specularColor.isIgnored() || editMtl->emissiveColor.isIgnored() || editMtl->shininess.isIgnored() || editMtl->transparency.isIgnored() ){ // Create a path to the material SoPath *mtlPath; if ( (! ignoreNodekit) && selectionPath->getTail()->isOfType( SoBaseKit::getClassTypeId() )) { SoBaseKit *kit = (SoBaseKit *) ((SoNodeKitPath *)selectionPath)->getTail(); mtlPath = kit->createPathToPart( "material", TRUE, selectionPath ); mtlPath->ref(); } else { if ( !isTailGroup ) { // CASE 1: path-tail was NOT 'group' -- copy all but last entry mtlPath = selectionPath->copy(0, pathLength - 1); } else { // CASE 2: path-tail was 'group' -- copy all of editPath mtlPath = selectionPath->copy(0, pathLength); } mtlPath->ref(); // add the material to the end of the path int mtlIndex = group->findChild(editMtl); mtlPath->append( mtlIndex ); } // Pass the material node to an accumulate state callback // that will load any 'ignored' values with their inherited values. SoCallbackAction cba; cba.addPreTailCallback( SoSceneViewer::findMtlPreTailCB, editMtl); cba.apply( mtlPath ); mtlPath->unref(); } return( editMtl ); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback used by 'findMaterialForAttach' as part of the accumulate state // action. Returns 'PRUNE', which tells the action not to draw the // shape as part of the accum state action. // editor to. // // Use: private // SoCallbackAction::Response SoSceneViewer::findMtlPreTailCB( void *data, SoCallbackAction *accum, const SoNode * ) // //////////////////////////////////////////////////////////////////////// { SoMaterial *mtl = (SoMaterial *) data; SbColor ambient, diffuse, specular, emissive; float shininess, transparency; accum->getMaterial( ambient, diffuse, specular, emissive, shininess, transparency ); // inherit the accumulated values only in those fields being ignored. if ( mtl->ambientColor.isIgnored() ) mtl->ambientColor.setValue( ambient ); if ( mtl->diffuseColor.isIgnored() ) mtl->diffuseColor.setValue( diffuse ); if ( mtl->specularColor.isIgnored() ) mtl->specularColor.setValue( specular ); if ( mtl->emissiveColor.isIgnored() ) mtl->emissiveColor.setValue( emissive ); if ( mtl->shininess.isIgnored() ) mtl->shininess.setValue( shininess ); if ( mtl->transparency.isIgnored() ) mtl->transparency.setValue( transparency ); return SoCallbackAction::ABORT; } //////////////////////////////////////////////////////////////////////// // // Description: // Find the appropriate transform node in the scene graph for attaching a // transform editor or manipulator. // // How we treat the 'center' field of the transform node: // If we need to create a new transform node: // set the 'center' to be the geometric center of all objects // affected by that transform. // If we find a transform node that already exists: // 'center' will not be changed. // // Three possible cases: // [1] The path-tail is a node kit. Just ask the node kit for a path // to the part called "transform" // [2] The path-tail is NOT a group. We search the siblings of the path // tail (including the tail itself) from right to left for a node // that is affected by transforms (shapes, groups, lights,cameras). // We stop the search if we come to a transform node to the left of // the pathTail. If we find a node that IS affected by transform, // we will insert a transform node just before the path-tail. This is // because the editor should not affect nodes that appear // before attachPath in the scene graph. // [3] The path-tail IS a group. We search the children from left to // right for transform nodes. // We stop the search if we come to a transform node. // If we find a node that is affected by transform, we will insert a // transform just before this node. This is because the editor for a // group should affect ALL nodes within that group. // // NOTE: For the purposes of this routine, we consider SoSwitch as different // from other types of group. This is because we don't want to put // the new node underneath the switch, but next to it. // // Use: private // SoPath * SoSceneViewer::findTransformForAttach( const SoPath *target ) // path to start search from // //////////////////////////////////////////////////////////////////////// { int pathLength; SoPath *selectionPath; SoTransform *editXform; if ( ( selectionPath = (SoPath *) target ) == NULL ) { // // If no selection path is specified, then use the LAST path in the // current selection list. // selectionPath = (*selection)[selection->getNumSelected() - 1]; } pathLength = selectionPath->getLength(); if ( pathLength <= 0 ) { fprintf( stderr, "No objects currently selected...\n" ); return NULL; } #ifdef DEBUG if ( pathLength < 2 ) { fprintf( stderr, "Picked object has no parent...\n" ); return NULL; } #endif // find 'group' and try to find 'editXform' SoGroup *group; SoNode *node; int index, i; SbBool isTailGroup, isTailKit; SbBool existedBefore = FALSE; SoPath *pathToXform = NULL; editXform = NULL; isTailGroup = ( selectionPath->getTail()->isOfType(SoGroup::getClassTypeId() ) && !selectionPath->getTail()->isOfType(SoSwitch::getClassTypeId())); isTailKit = selectionPath->getTail()->isOfType(SoBaseKit::getClassTypeId()); // CASE 1: The path-tail is a node kit. if ( isTailKit ) { // Nodekits have their own built in policy for creating new transform // nodes. Allow them to contruct and return a path to it. SoBaseKit *kit = (SoBaseKit *) ((SoNodeKitPath *)selectionPath)->getTail(); // Before creating path, see if the transform part exists yet: if (SO_CHECK_PART(kit, "transform", SoTransform) != NULL) existedBefore = TRUE; if ((editXform = SO_GET_PART(kit, "transform", SoTransform)) != NULL) { pathToXform = kit->createPathToPart( "transform", TRUE, selectionPath ); pathToXform->ref(); } else { // This nodekit has no transform part. // Treat the object as if it were not a nodekit. isTailKit = FALSE; } } if ( !isTailGroup && !isTailKit ) { // // CASE 2: The path-tail is not a group. // 'group' becomes the second to last node in the path. // We search the path tail and its siblings from right to left for a // transform node. // We stop the search if we come to a 'movable' node // to the left of the pathTail. If we find a movable node, we // will insert a transform just before the path-tail. This is // because the manipulator should not affect objects that appear // before selectionPath in the scene graph. // group = (SoGroup *) selectionPath->getNode(pathLength - 2); index = group->findChild( selectionPath->getTail() ); for (i = index; (i >= 0) && (editXform == NULL); i--){ node = group->getChild(i); if (node->isOfType(SoTransform::getClassTypeId())) // found an SoMaterial editXform = (SoTransform *) node; else if ( i != index ) { if ( isAffectedByTransform( node ) ) break; } } if ( editXform == NULL ) { existedBefore = FALSE; editXform = new SoTransform; group->insertChild( editXform, index ); } else existedBefore = TRUE; } else if ( !isTailKit ) { // CASE 3: The path-tail is a group. // 'group' becomes the path tail // We search the children from left to right for transform nodes. // We stop the search if we come to a movable node. // If we find a movable node, we will insert a transform just // before this node. This is because the editor // for a group should affect ALL objects within that group. // group = (SoGroup *) selectionPath->getTail(); for (i = 0; (i < group->getNumChildren()) && (editXform == NULL); i++ ) { node = group->getChild(i); if (node->isOfType(SoTransform::getClassTypeId())) editXform = (SoTransform *) node; else if ( isAffectedByTransform( node ) ) break; } if ( editXform == NULL ) { existedBefore = FALSE; editXform = new SoTransform; group->insertChild( editXform, i ); } else existedBefore = TRUE; } // If we don't have a path yet (i.e., we weren't handed a nodekit path) // create the 'pathToXform' // by copying editPath and making the last node in the path be editXform if ( pathToXform == NULL ) { if ( !isTailGroup ) // CASE 2: path-tail was NOT 'group' -- copy all but last entry pathToXform = selectionPath->copy(0, pathLength - 1); else // CASE 3: path-tail was 'group' -- copy all of editPath pathToXform = selectionPath->copy(0, pathLength); pathToXform->ref(); // add the transform to the end int xfIndex = group->findChild(editXform); pathToXform->append( xfIndex ); } // Now. If we created the transform node right here, right now, then // we will set the 'center' field based on the geometric center. We // don't do this if we didn't create the transform, because "maybe it // was that way for a reason." if ( existedBefore == FALSE ) { // First, find 'applyPath' by popping nodes off the path until you // reach a separator. This path will contain all nodes affected by // the transform at the end of 'pathToXform' SoFullPath *applyPath = (SoFullPath *) pathToXform->copy(); applyPath->ref(); for (int i = (applyPath->getLength() - 1); i >0; i-- ) { if (applyPath->getNode(i)->isOfType( SoSeparator::getClassTypeId())) break; applyPath->pop(); } // Next, apply a bounding box action to applyPath, and reset the // bounding box just before the tail of 'pathToXform' (which is just // the editXform). This will assure that the only things included in // the resulting bbox will be those affected by the editXform. SoGetBoundingBoxAction bboxAction(currentViewer->getViewportRegion()); bboxAction.setResetPath(pathToXform,TRUE,SoGetBoundingBoxAction::BBOX ); bboxAction.apply(applyPath); applyPath->unref(); // Get the center of the bbox in world space... SbVec3f worldBoxCenter = bboxAction.getBoundingBox().getCenter(); // Convert it into local space of the transform... SbVec3f localBoxCenter; SoGetMatrixAction ma(currentViewer->getViewportRegion()); ma.apply( pathToXform ); ma.getInverse().multVecMatrix( worldBoxCenter, localBoxCenter ); // Finally, set the center value... editXform->center.setValue( localBoxCenter ); } pathToXform->unrefNoDelete(); return( pathToXform ); } //////////////////////////////////////////////////////////////////////// // // Description: // Create a material editor for the currently selected object. // // Use: private void SoSceneViewer::createMaterialEditor() // //////////////////////////////////////////////////////////////////////// { if (materialEditor == NULL) materialEditor = new SoXtMaterialEditor; materialEditor->show(); materialEditor->attach( findMaterialForAttach( NULL ) ); } //////////////////////////////////////////////////////////////////////// // // Description: // Create a transform editor for the currently selected object // // Use: private void SoSceneViewer::createTransformSliderSet() // //////////////////////////////////////////////////////////////////////// { SoPath *editTransformPath; SoTransform *editTransform; // get path to a transform to edit if ( ( editTransformPath = findTransformForAttach( NULL )) == NULL ) return; // the tail of the path is a transform for us! editTransformPath->ref(); editTransform =(SoTransform *) ((SoFullPath *)editTransformPath)->getTail(); editTransformPath->unref(); // Nuke the old slider set and get a new one if (transformSliderSet == NULL) transformSliderSet = new SoXtTransformSliderSet(); transformSliderSet->setNode(editTransform); transformSliderSet->show(); } //////////////////////////////////////////////////////////////////////// // // Description: // Set fog on/off. Leave density alone - when default, the environment // node will do something useful to make the fog look good. // // Use: private void SoSceneViewer::setFog(SbBool flag) // //////////////////////////////////////////////////////////////////////// { fogFlag = flag; if (fogFlag) environment->fogType.setValue( SoEnvironment::HAZE ); // purple ? else environment->fogType.setValue( SoEnvironment::NONE ); } //////////////////////////////////////////////////////////////////////// // // Description: // Set AA-ing on/off. // // Use: private void SoSceneViewer::setAntialiasing(SbBool flag) // //////////////////////////////////////////////////////////////////////// { antialiasingFlag = flag; if (antialiasingFlag) currentViewer->setAntialiasing( TRUE, 3 ); else currentViewer->setAntialiasing( FALSE, 1 ); } //////////////////////////////////////////////////////////////////////// // // Description: // Invokes color editor on ambient lighting color. void SoSceneViewer::editAmbientColor() // //////////////////////////////////////////////////////////////////////// { if ( ambientColorEditor == NULL ) { ambientColorEditor = new MyColorEditor; ambientColorEditor->setTitle( "Ambient Lighting" ); ambientColorEditor->addColorChangedCallback( SoSceneViewer::ambientColorCallback, this ); } // Normalize ambient intensity SbColor ambCol; ambCol = environment->ambientColor.getValue(); ambCol *= environment->ambientIntensity.getValue(); environment->ambientIntensity.setValue( 1.0 ); environment->ambientColor.setValue( ambCol ); ignoreCallback = TRUE; ambientColorEditor->setColor( environment->ambientColor.getValue() ); ignoreCallback = FALSE; ambientColorEditor->show(); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback proc invoked by the color editor, this changes the scene's // ambient lighting color. // // Use: static, private // void SoSceneViewer::ambientColorCallback(void *userData, const SbColor *color) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; if (sv->ignoreCallback) return; sv->environment->ambientColor.setValue( *color ); } //////////////////////////////////////////////////////////////////////// // // Description: // Invokes color editor on background color. void SoSceneViewer::editBackgroundColor() // //////////////////////////////////////////////////////////////////////// { if ( backgroundColorEditor == NULL ) { backgroundColorEditor = new MyColorEditor; backgroundColorEditor->setTitle( "Background Color" ); backgroundColorEditor->addColorChangedCallback( SoSceneViewer::backgroundColorCallback, this ); } ignoreCallback = TRUE; backgroundColorEditor->setColor(getBackgroundColor()); ignoreCallback = FALSE; backgroundColorEditor->show(); } //////////////////////////////////////////////////////////////////////// // // Description: // Callback proc invoked by the color editor, this changes the current // viewer's background color. // // Use: static, private // void SoSceneViewer::backgroundColorCallback(void *userData, const SbColor *c) // //////////////////////////////////////////////////////////////////////// { //??? should be using the Roxy color editor, not the So color editor SoSceneViewer *sv = (SoSceneViewer *) userData; if (sv->ignoreCallback) return; sv->currentViewer->setBackgroundColor( *c ); // keep fog color up to date with bkg color sv->environment->fogColor.setValue( *c ); } //////////////////////////////////////////////////////////////////////// // // Description: // This will: // [a] find the last sceneKit camera that is being looked through // in the scene and return a pointer to it. // // Use: private SoCamera * SoSceneViewer::getLastSceneKitCamera(SoGroup *root) // //////////////////////////////////////////////////////////////////////// { // Find scene kits under the root node and loop backwards: SoSearchAction sa; sa.setType(SoSceneKit::getClassTypeId()); sa.setInterest(SoSearchAction::ALL); sa.apply(root); SoPathList paths = sa.getPaths(); for (int i = paths.getLength() - 1; i >= 0; i--) { SoPath *p = paths[i]; if (p->getTail()->isOfType( SoSceneKit::getClassTypeId() ) ) { SoSceneKit *sceneKit = (SoSceneKit *) p->getTail(); int camNum = sceneKit->getCameraNumber(); SoNodeKitListPart *camList = (SoNodeKitListPart *) sceneKit->getPart("cameraList",FALSE); if (camList && camNum >= 0) { SoCameraKit *camKit = (SoCameraKit *)camList->getChild( camNum ); if (camKit) return( (SoCamera *) camKit->getPart( "camera", FALSE )); } } } return NULL; } //////////////////////////////////////////////////////////////////////// // // Description: // This will: // [a] find the last sceneKit, its camera number, and replace the // given camera for that node. // // Use: private void SoSceneViewer::setLastSceneKitCamera(SoGroup *root, SoCamera *theCam) // //////////////////////////////////////////////////////////////////////// { // Find scene kits under the root node and loop backwards: SoSearchAction sa; sa.setType(SoSceneKit::getClassTypeId()); sa.setInterest(SoSearchAction::ALL); sa.apply(root); SoPathList paths = sa.getPaths(); for (int i = paths.getLength() - 1; i >= 0; i--) { SoPath *p = paths[i]; if (p->getTail()->isOfType( SoSceneKit::getClassTypeId() ) ) { SoSceneKit *sceneKit = (SoSceneKit *) p->getTail(); int camNum = sceneKit->getCameraNumber(); SoNodeKitListPart *camList = (SoNodeKitListPart *) sceneKit->getPart("cameraList",FALSE); if (camList && camNum >= 0) { SoCameraKit *camKit = (SoCameraKit *)camList->getChild( camNum ); if (camKit) { camKit->setPart("camera",theCam); return; } } } } return; } //////////////////////////////////////////////////////////////////////// // // Description: // This will: // [a] find scene kits if under the root node. // [b] Save the camera number settings of the kits in a dictionary. // [c] Switch off the scenekit cameras. // // Use: private void SoSceneViewer::switchOffSceneKitCameras(SoGroup *root) // //////////////////////////////////////////////////////////////////////// { // [a] find scene kits if under the root node. SoSearchAction sa; sa.setType(SoSceneKit::getClassTypeId()); sa.setInterest(SoSearchAction::ALL); sa.apply(root); // Deal with each kit: SoPathList paths = sa.getPaths(); for (int i = paths.getLength() - 1; i >= 0; i--) { SoPath *p = paths[i]; if (p->getTail()->isOfType( SoSceneKit::getClassTypeId() ) ) { SoSceneKit *sceneKit = (SoSceneKit *) p->getTail(); int *camNumPtr = new int; *camNumPtr = sceneKit->getCameraNumber(); // [b] Save the camera number settings of the kits in a dictionary. sceneKitCamNumDict->enter((unsigned long)sceneKit, (void *)camNumPtr); // [c] Switch off the scenekit cameras. sceneKit->setCameraNumber( SO_SWITCH_NONE ); } } } //////////////////////////////////////////////////////////////////////// // // Description: // This will: // [a] find scene kits if under the root node. // [b] Find the camera number settings of the kits from a dictionary. // [c] Restore the sceneKit camera numbers. // // Use: private void SoSceneViewer::restoreSceneKitCameras(SoGroup *root) // //////////////////////////////////////////////////////////////////////// { // [a] find scene kits if under the root node. SoSearchAction sa; sa.setType(SoSceneKit::getClassTypeId()); sa.setInterest(SoSearchAction::ALL); sa.apply(root); // Deal with each kit: SoPathList paths = sa.getPaths(); for (int i = paths.getLength() - 1; i >= 0; i--) { SoPath *p = paths[i]; if (p->getTail()->isOfType( SoSceneKit::getClassTypeId() ) ) { SoSceneKit *sceneKit = (SoSceneKit *) p->getTail(); void *myPtr; int camNum; if (sceneKitCamNumDict->find((unsigned long)sceneKit, myPtr)) camNum = *((int *)myPtr); else camNum = SO_SWITCH_NONE; sceneKit->setCameraNumber( camNum ); } } } //////////////////////////////////////////////////////////////////////// // // Description: // Copies from given camera into this viewer's camera, if types match. // // Use: private void SoSceneViewer::setCameraFields( SoCamera *fromCam ) // //////////////////////////////////////////////////////////////////////// { SoCamera *myCam = getCamera(); if ( ! myCam || ! fromCam ) return; // Types must match! if ( myCam->getTypeId() != fromCam->getTypeId() ) return; myCam->viewportMapping = fromCam->viewportMapping.getValue(); myCam->position = fromCam->position.getValue(); myCam->orientation = fromCam->orientation.getValue(); myCam->aspectRatio = fromCam->aspectRatio.getValue(); myCam->nearDistance = fromCam->nearDistance.getValue(); myCam->farDistance = fromCam->farDistance.getValue(); myCam->focalDistance = fromCam->focalDistance.getValue(); if ( myCam->isOfType( SoPerspectiveCamera::getClassTypeId() )) { ((SoPerspectiveCamera *)myCam)->heightAngle = ((SoPerspectiveCamera *)fromCam)->heightAngle.getValue(); } else if ( myCam->isOfType( SoOrthographicCamera::getClassTypeId() )) { ((SoOrthographicCamera *)myCam)->height = ((SoOrthographicCamera *)fromCam)->height.getValue(); } } //////////////////////////////////////////////////////////////////////// // // Description: // This will remove any cameras under root. // // Use: private void SoSceneViewer::removeCameras(SoGroup *root) // //////////////////////////////////////////////////////////////////////// { SoSearchAction sa; sa.setType(SoCamera::getClassTypeId()); sa.setInterest(SoSearchAction::ALL); sa.apply(root); // remove those cameras! SoPathList paths = sa.getPaths(); for (int i = 0; i < paths.getLength(); i++) { SoPath *p = paths[i]; int cam = p->getIndexFromTail(0); SoGroup *group = (SoGroup *) p->getNodeFromTail(1); group->removeChild(cam); // See if the parent of this camera is a switch node with whichChild set // to render the camera. With the camera gone, the switch has nothing // to render. if (group->isOfType(SoSwitch::getClassTypeId())) { SoSwitch *sw = (SoSwitch *) group; if (sw->whichChild.getValue() == cam) sw->whichChild.setValue(SO_SWITCH_NONE); } } } //////////////////////////////////////////////////////////////////////// // // Description: // Reads the given file and insert the geometry under the selection // node. If the node didn't have any children, the viewAll() method is // automatically called. // // Use: private SbBool SoSceneViewer::readFile(const char *filename) // //////////////////////////////////////////////////////////////////////// { SbBool hadNoChildren = (selection->getNumChildren() == 0); // Try reading the file SbString errorMessage; SoSeparator *sep = MyFileRead(filename, errorMessage); // add nodes under selection, not sceneGraph if (sep != NULL) selection->addChild(sep); // display error dialog if there were reading errors else { SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", (char *) errorMessage.getString()); return FALSE; } // remove any cameras under selection which were just added SoCamera *camFromScene = getLastSceneKitCamera(selection); switchOffSceneKitCameras(selection); removeCameras(selection); if (hadNoChildren) { viewAll(); saveHomePosition(); } // If scene had sceneKit with a camera, copy its values into our camera. if (camFromScene) setCameraFields( camFromScene ); return TRUE; } //////////////////////////////////////////////////////////////////////// // // Description: // Read environment data. We expect the following nodes: // // Group { // Label { "SoSceneViewer Environment v3.0" } // Camera {} // Environment {} // LightGroup { // Switch { DirectionalLight } # 1 // ... // Switch { DirectionalLight } # 6 // } // DirectionalLight {} # optional headlight // } // // Use: private SbBool SoSceneViewer::readEnvFile(const char *filename) // //////////////////////////////////////////////////////////////////////// { SoInput in; if (! in.openFile(filename)) { // display an error dialog char str[100]; strcpy(str, "Error opening file: "); strcat(str, filename); SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str); return FALSE; } SoNode *n; SoLabel *l = NULL; SbBool isValid = FALSE; SbBool ok; if ((ok = SoDB::read(&in, n)) && n != NULL) { // we expect a label first n->ref(); if (n->isOfType(SoLabel::getClassTypeId())) { l = (SoLabel *) n; isValid = (strcmp(l->label.getValue().getString(), SV_ENV_LABEL) == 0); } n->unref(); } else if (!ok) { // display error dialog if there were reading errors char str[100]; strcpy(str, "Error reading file: "); strcat(str, filename); SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str); return FALSE; } // if ok, read the rest. if (isValid) { // Camera if (SoDB::read(&in, n) != FALSE && (n != NULL)) { n->ref(); if (n->isOfType(SoCamera::getClassTypeId())) { // replace the old camera with the new camera and // re-attach the viewer. SoCamera *newCamera = (SoCamera *) n; SoCamera *oldCamera = getCamera(); SoSearchAction sa; sa.setNode(oldCamera); sa.apply(sceneGraph); SoFullPath *fullCamPath = (SoFullPath *) sa.getPath(); if (fullCamPath) { SoGroup *parent = (SoGroup *)fullCamPath->getNode(fullCamPath->getLength() - 2); parent->insertChild(newCamera, parent->findChild(oldCamera)); setCamera(newCamera); if (parent->findChild(oldCamera) >= 0) parent->removeChild(oldCamera); } #if DEBUG else SoDebugError::post("SoSceneViewer::readEnvFile", "cannot find camera in graph"); #endif } n->unref(); } // Environment if (SoDB::read(&in, n) != FALSE && (n != NULL)) { n->ref(); if (n->isOfType(SoEnvironment::getClassTypeId())) { lightsCameraEnvironment->replaceChild(environment, n); environment = (SoEnvironment *) n; } n->unref(); } // Light group if (SoDB::read(&in, n) != FALSE && (n != NULL)) { n->ref(); if (n->isOfType(SoGroup::getClassTypeId())) { // remove all of the existing lights int i; for (i = lightDataList.getLength(); i > 0; i--) removeLight( (SvLightData *) lightDataList[i-1] ); lightsCameraEnvironment->replaceChild(lightGroup, n); lightGroup = (SoGroup *) n; // This was busted. It was looking for a light as child 0, // but the scale and scaleInverse made it think no light was // there. So now, we do this right... // We'll just check for the light as any old child. // This way it's okay to add a translation node under that // switch too, so we can translate the manips as well. // This allows as to place the directional light manips. for (i=0; i < lightGroup->getNumChildren(); i++) { SoNode *node = lightGroup->getChild(i); if (node->isOfType(SoSwitch::getClassTypeId())) { SoSwitch *sw = (SoSwitch *) node; SbBool addedIt = FALSE; for (int j = 0; addedIt == FALSE && j < sw->getNumChildren(); j++ ) { node = sw->getChild(j); if (node->isOfType(SoLight::getClassTypeId())) { addLightEntry((SoLight *)node, sw); addedIt = TRUE; } } } } } n->unref(); } // Headlight (optional) - if not there, turn headlight off if (SoDB::read(&in, n) != FALSE && (n != NULL)) { n->ref(); if (n->isOfType(SoDirectionalLight::getClassTypeId())) { SoDirectionalLight *headlight = getHeadlight(); SoDirectionalLight *newLight = (SoDirectionalLight *) n; if (headlight != NULL) { headlight->intensity.setValue(newLight->intensity.getValue()); headlight->color.setValue(newLight->color.getValue()); headlight->direction.setValue(newLight->direction.getValue()); setHeadlight(TRUE); } } n->unref(); } else setHeadlight(FALSE); } else { fprintf(stderr, "Sorry, file is not formatted correctly\n"); } return TRUE; } //////////////////////////////////////////////////////////////////////// // // Description: // This routine is called to get a file name using the // standard file dialog. // // Use: private void SoSceneViewer::getFileName() // //////////////////////////////////////////////////////////////////////// { // use a motif file selection dialog if (fileDialog == NULL) { Arg args[5]; int n = 0; // unmanage when ok/cancel are pressed XtSetArg(args[n], XmNautoUnmanage, TRUE); n++; fileDialog = XmCreateFileSelectionDialog( XtParent(mgrWidget), "File Dialog", args, n); XtAddCallback(fileDialog, XmNokCallback, (XtCallbackProc)SoSceneViewer::fileDialogCB, (XtPointer)this); } // manage the dialog XtManageChild(fileDialog); } //////////////////////////////////////////////////////////////////////// // // Description: // Motif file dialog callback. // void SoSceneViewer::fileDialogCB(Widget, SoSceneViewer *sv, XmFileSelectionBoxCallbackStruct *data) // //////////////////////////////////////////////////////////////////////// { // Get the file name char *filename; XmStringGetLtoR(data->value, (XmStringCharSet) XmSTRING_DEFAULT_CHARSET, &filename); // Use that file sv->doFileIO(filename); XtFree(filename); } //////////////////////////////////////////////////////////////////////// // // Description: // detach everything and nuke the existing scene. // // Use: private void SoSceneViewer::deleteScene() // //////////////////////////////////////////////////////////////////////// { // deselect everything (also detach manips) selection->deselectAll(); // remove the geometry under the selection node for (int i = selection->getNumChildren(); i>0; i--) selection->removeChild(i-1); } //////////////////////////////////////////////////////////////////////// // // Description: // Read/Write to the given file name, given the current file mode. // // Use: private SbBool SoSceneViewer::doFileIO(const char *file) // //////////////////////////////////////////////////////////////////////// { SbBool okFile = TRUE; switch (fileMode) { case SV_FILE_OPEN: deleteScene(); okFile = readFile(file); // If we've already made lights, re-calculate size for light // manips based on size of new scene. if (calculatedLightManipSize) calculateLightManipSize(); break; case SV_FILE_IMPORT: okFile = readFile(file); // If we've already made lights, re-calculate size for light // manips based on size of new scene. if (calculatedLightManipSize) calculateLightManipSize(); break; case SV_FILE_SAVE_AS: okFile = writeFile(file); break; case SV_FILE_READ_ENV: readEnvFile(file); break; case SV_FILE_SAVE_ENV: { // Run through the lights. If any light is a directionalLightManip, // transfer its translation into the SvLightData, since that // info will not write out. for (int i=0; i < lightDataList.getLength(); i++) transferDirectionalLightLocation((SvLightData *) lightDataList[i]); writeEnvFile(file); } break; default: fprintf(stderr, "Wrong file mode %d passed!\n", fileMode); return FALSE; } // save the new file name so we can simply use "Save" instead of // "Save As" next time around. if (fileMode == SV_FILE_OPEN || fileMode == SV_FILE_SAVE_AS) { // save the current file name delete fileName; if (okFile && file != NULL) fileName = strdup(file); else fileName = NULL; } // enable/disable cmd key shortcuts and menu items updateCommandAvailability(); return okFile; } //////////////////////////////////////////////////////////////////////// // // Description: // Saves the scene to the current file. // // Use: private void SoSceneViewer::save() // //////////////////////////////////////////////////////////////////////// { if (fileName != NULL) { SbBool ok = writeFile(fileName); if (!ok) { delete fileName; fileName = NULL; } } else { fileMode = SV_FILE_SAVE_AS; getFileName(); } } //////////////////////////////////////////////////////////////////////// // // Description: // Removes the attached light manips geometry from the scene. This // is used for file writting,... // // Use: private void SoSceneViewer::removeAttachedLightManipGeometry() // //////////////////////////////////////////////////////////////////////// { for (int i = 0; i < lightDataList.getLength(); i++ ) { SvLightData *data = (SvLightData *) lightDataList[i]; // We'll be putting everything back later, so make a note of this... data->shouldBeManip = data->isManip(); if ( data->isManip() == TRUE ) editLight(data, FALSE); } } //////////////////////////////////////////////////////////////////////// // // Description: // Add the attached light manips geometry back into the scene. This // is called after the geometry has been temporaly revomed (used for file // writting). // // Use: private void SoSceneViewer::addAttachedLightManipGeometry() // //////////////////////////////////////////////////////////////////////// { for (int i = 0; i < lightDataList.getLength(); i++ ) { SvLightData *data = (SvLightData *) lightDataList[i]; if ( data->isManip() != data->shouldBeManip ) editLight(data, data->shouldBeManip); } } //////////////////////////////////////////////////////////////////////// // // Description: // Write the nodes under the selection node to the given file name. // // Use: private SbBool SoSceneViewer::writeFile(const char *filename) // //////////////////////////////////////////////////////////////////////// { SoWriteAction wa; if (! wa.getOutput()->openFile(filename)) { // display an error dialog char str[100]; strcpy(str, "Error creating file: "); strcat(str, filename); SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str); return FALSE; } // temporarily replace all manips with regular transform nodes. removeManips(); // restore the cameraNum settings of any scenekits. restoreSceneKitCameras(selection); // Copy our camera into the last sceneKit. setLastSceneKitCamera(selection, (SoCamera *)getCamera()->copy()); // write out all the children of the selection node for (int i = 0; i < selection->getNumChildren(); i++) wa.apply(selection->getChild(i)); wa.getOutput()->closeFile(); // Switch the sceneKit cameras back off: switchOffSceneKitCameras(selection); // Now put the manips back in the scene graph. restoreManips(); return TRUE; } //////////////////////////////////////////////////////////////////////// // // Description: // Write the Enviroment nodes (camera and lights) to the given // file name. // // Use: private SbBool SoSceneViewer::writeEnvFile(const char *filename) // //////////////////////////////////////////////////////////////////////// { SoWriteAction wa; if (! wa.getOutput()->openFile(filename)) { // display an error dialog char str[100]; strcpy(str, "Error creating file: "); strcat(str, filename); SoXt::createSimpleErrorDialog(mgrWidget, "File Error Dialog", str); return FALSE; } // write out the environment including the headlight wa.apply(envLabel); wa.apply(getCamera()); wa.apply(environment); wa.apply(lightGroup); if (isHeadlight()) wa.apply(getHeadlight()); wa.getOutput()->closeFile(); return TRUE; } //////////////////////////////////////////////////////////////////////// // // Description: // Print the scene using a custom print dialog. // // Use: private void SoSceneViewer::print() // //////////////////////////////////////////////////////////////////////// { if (printDialog == NULL) { printDialog = new SoXtPrintDialog; printDialog->setTitle("SceneViewer Printing"); printDialog->setBeforePrintCallback( SoSceneViewer::beforePrintCallback, (void *) this); printDialog->setAfterPrintCallback( SoSceneViewer::afterPrintCallback, (void *) this); } // // Send the render area size and scene graph to the print dialog // Widget widget = getRenderAreaWidget(); if (widget != NULL) { Arg args[2]; int n = 0; SbVec2s sz; XtSetArg(args[n], XtNwidth, &sz[0]); n++; XtSetArg(args[n], XtNheight, &sz[1]); n++; XtGetValues(widget, args, n); printDialog->setPrintSize(sz); } printDialog->show(); } //////////////////////////////////////////////////////////////////////// // // Description: // Temporarily remove manips from the scene. // Restore them with a call to restoreManips(). // // Use: private void SoSceneViewer::removeManips() // //////////////////////////////////////////////////////////////////////// { // temporarily replace all manips with regular transform nodes. for (int m = 0; m < maniplist->getLength(); m++ ) { SoTransformManip *manip = maniplist->getManip(m); SoPath *xfPath = maniplist->getXfPath(m); manip->replaceManip(xfPath, NULL); } } //////////////////////////////////////////////////////////////////////// // // Description: // Restore manips that were removed with removeManips(). // // Use: private void SoSceneViewer::restoreManips() // //////////////////////////////////////////////////////////////////////// { // Now put the manips back in the scene graph. for (int m = 0; m < maniplist->getLength(); m++ ) { SoTransformManip *manip = maniplist->getManip(m); SoPath *xfPath = maniplist->getXfPath(m); manip->replaceNode(xfPath); } } //////////////////////////////////////////////////////////////////////// // // Description: // Temporarily remove manips from the scene. They // will all be restored after the printing is done. // // Use: private, static void SoSceneViewer::beforePrintCallback(void *uData, SoXtPrintDialog *) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *)uData; // temporarily replace all manips with regular transforms. sv->removeManips(); // Do the same for all the light manips sv->removeAttachedLightManipGeometry(); // if the current viewer is the examiner viewer, turn the // feedback axis off while we print if (sv->whichViewer == SV_VWR_EXAMINER) { SoXtExaminerViewer *exam = (SoXtExaminerViewer *) sv->currentViewer; sv->feedbackShown = exam->isFeedbackVisible(); exam->setFeedbackVisibility(FALSE); } // set the scene to print sv->printDialog->setSceneGraph(sv->sceneGraph); } //////////////////////////////////////////////////////////////////////// // // Description: // Called after printing is done. Add the manips back into the // scene. // // Use: private, static void SoSceneViewer::afterPrintCallback(void *uData, SoXtPrintDialog *) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *)uData; // put the manips back in the scene graph. sv->restoreManips(); // put the light manips back in the scene graph. sv->addAttachedLightManipGeometry(); // restor the examiner feedback if (sv->whichViewer == SV_VWR_EXAMINER) { SoXtExaminerViewer *exam = (SoXtExaminerViewer *) sv->currentViewer; exam->setFeedbackVisibility( sv->feedbackShown ); } } //////////////////////////////////////////////////////////////////////// // // Description: // Static routine for processing topbar menu events. // When the menu is created, it stores pointer to the SoSceneViewer // in the client_data, so that we can tell which SoSceneViewer needs // the event. // // Use: private, static // void SoSceneViewer::processTopbarEvent( Widget, // Which widget? I don't care SoSceneViewerData *data, // Pointer to button/SoSceneViewer XmAnyCallbackStruct *cb ) // X garbage // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = data->classPt; Time eventTime = cb->event->xbutton.time; switch (data->id) { // // File // case SV_FILE_ABOUT: sv->showAboutDialog(); break; case SV_FILE_NEW: sv->deleteScene(); delete sv->fileName; sv->fileName = NULL; break; case SV_FILE_OPEN: case SV_FILE_IMPORT: case SV_FILE_SAVE_AS: case SV_FILE_READ_ENV: case SV_FILE_SAVE_ENV: sv->fileMode = data->id; sv->getFileName(); break; case SV_FILE_SAVE: sv->save(); break; case SV_FILE_PRINT: sv->print(); break; case SV_FILE_QUIT: delete sv; exit(0); break; // // Edit // case SV_EDIT_PICK_PARENT: sv->pickParent(); break; case SV_EDIT_PICK_ALL: sv->pickAll(); break; case SV_EDIT_CUT: // Remove manipulators before cutting the selection. sv->detachManipFromAll(); sv->clipboard->copy((SoPathList *)sv->selection->getList(), eventTime); sv->destroySelectedObjects(); sv->updateCommandAvailability(); break; case SV_EDIT_COPY: // Remove manipulators while copying the selection. sv->removeManips(); sv->clipboard->copy((SoPathList *)sv->selection->getList(), eventTime); sv->restoreManips(); break; case SV_EDIT_PASTE: sv->clipboard->paste(eventTime, pasteDoneCB, sv); break; case SV_EDIT_DELETE: sv->destroySelectedObjects(); sv->updateCommandAvailability(); break; // // Viewing // case SV_VIEW_PICK: sv->setViewing(! sv->isViewing()); break; #ifdef EXPLORER case SV_VIEW_USER: sv->userModeFlag = !sv->userModeFlag; if (sv->userModeFlag) sv->currentViewer->setEventCallback(sv->userModeCB, sv->userModedata); else sv->currentViewer->setEventCallback(NULL, NULL); break; #endif case SV_VIEW_EXAMINER: sv->switchToViewer(SV_VWR_EXAMINER); break; case SV_VIEW_WALK: sv->switchToViewer(SV_VWR_WALK); break; case SV_VIEW_PLANE: sv->switchToViewer(SV_VWR_PLANE); break; case SV_VIEW_FLY: sv->switchToViewer(SV_VWR_FLY); break; case SV_VIEW_SELECTION: sv->viewSelection(); break; case SV_VIEW_SCREEN_TRANSPARENCY: sv->setTransparencyType(SoGLRenderAction::SCREEN_DOOR); break; case SV_VIEW_BLEND_TRANSPARENCY: sv->setTransparencyType(SoGLRenderAction::BLEND); break; case SV_VIEW_DELAY_BLEND_TRANSPARENCY: sv->setTransparencyType(SoGLRenderAction::DELAYED_BLEND); break; case SV_VIEW_SORT_BLEND_TRANSPARENCY: sv->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_BLEND); break; case SV_VIEW_FOG: sv->setFog(! sv->fogFlag); break; case SV_VIEW_ANTIALIASING: sv->setAntialiasing(! sv->antialiasingFlag); break; case SV_VIEW_BKG_COLOR: sv->editBackgroundColor(); break; // // Editors // case SV_EDITOR_MATERIAL: sv->createMaterialEditor(); break; case SV_EDITOR_TRANSFORM: sv->createTransformSliderSet(); break; case SV_EDITOR_COLOR: sv->createColorEditor(); break; // // Selection // case SV_SEL_SINGLE_SELECT: sv->selection->policy.setValue(SoSelection::SINGLE); break; case SV_SEL_TOGGLE_SELECT: sv->selection->policy.setValue(SoSelection::TOGGLE); break; case SV_SEL_SHIFT_SELECT: sv->selection->policy.setValue(SoSelection::SHIFT); break; // // Manips // case SV_MANIP_TRANSFORMER: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip = (sv->curManip == SV_TRANSFORMER) ? SV_NONE : SV_TRANSFORMER; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_TRACKBALL: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip = (sv->curManip == SV_TRACKBALL) ? SV_NONE : SV_TRACKBALL; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_HANDLEBOX: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip = (sv->curManip == SV_HANDLEBOX) ? SV_NONE : SV_HANDLEBOX; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_JACK: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip = (sv->curManip == SV_JACK) ? SV_NONE : SV_JACK; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_CENTERBALL: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip =(sv->curManip == SV_CENTERBALL) ? SV_NONE : SV_CENTERBALL; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_XFBOX: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip = (sv->curManip == SV_XFBOX) ? SV_NONE : SV_XFBOX; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_TABBOX: sv->highlightRA->setVisible(FALSE); // highlight visible when no manip sv->curManip = (sv->curManip == SV_TABBOX) ? SV_NONE : SV_TABBOX; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; case SV_MANIP_NONE: sv->highlightRA->setVisible(TRUE); // highlight visible when no manip sv->curManip = SV_NONE; if ( sv->curManipReplaces ) sv->detachManipFromAll(); break; case SV_MANIP_REPLACE_ALL: // Toggle the value of 'curManipReplaces' sv->curManipReplaces = ( sv->curManipReplaces == TRUE) ? FALSE : TRUE; if ( sv->curManipReplaces ) sv->replaceAllManips( sv->curManip ); break; // // Lights // case SV_LIGHT_AMBIENT_EDIT: sv->editAmbientColor(); break; case SV_LIGHT_ADD_DIRECT: sv->addLight(new SoDirectionalLightManip); break; case SV_LIGHT_ADD_POINT: sv->addLight(new SoPointLightManip); break; case SV_LIGHT_ADD_SPOT: { // Set the dropOffRate to be non-zero, or it will always work // like a point light. SoSpotLightManip *newSpotManip = new SoSpotLightManip; newSpotManip->dropOffRate = .01; sv->addLight(newSpotManip); } break; case SV_LIGHT_TURN_ON: case SV_LIGHT_TURN_OFF: { SbBool onFlag = (data->id == SV_LIGHT_TURN_ON); for (int i=0; i < sv->lightDataList.getLength(); i++) sv->turnLightOnOff((SvLightData *) sv->lightDataList[i], onFlag); sv->turnLightOnOff( sv->headlightData, onFlag); } break; case SV_LIGHT_SHOW_ALL: case SV_LIGHT_HIDE_ALL: { SbBool onFlag = (data->id == SV_LIGHT_SHOW_ALL); for (int i=0; i < sv->lightDataList.getLength(); i++) sv->editLight((SvLightData *) sv->lightDataList[i], onFlag); } break; } // endswitch( topbar button ) } //////////////////////////////////////////////////////////////////////// // // Description: // Adds the given light to the scene and to the menu. // // Use: private // void SoSceneViewer::addLight(SoLight *light) // //////////////////////////////////////////////////////////////////////// { // Calculate the light manip size if necessary. // Do this before adding the light into the scene, // since if the light is a manip, its default size might influence the // scene size. if (!calculatedLightManipSize) calculateLightManipSize(); // create the switch and light node and add it to the scene SoSwitch *lightSwitch = new SoSwitch; lightGroup->addChild(lightSwitch); lightSwitch->addChild(light); SWITCH_LIGHT_ON(lightSwitch); // add the light entry for the new light SvLightData *data = addLightEntry(light, lightSwitch); // // Try to come up with some meaningfull default position base // of the current camera view volume. // SoCamera *vwrCamera = getCamera(); // don't cache this in the class SbViewVolume vv = vwrCamera->getViewVolume(0.0); SbVec3f forward = - vv.zVector(); SbVec3f center = vwrCamera->position.getValue() + forward * (vwrCamera->nearDistance.getValue() + vwrCamera->farDistance.getValue()) / 2.0; SbVec3f position( vv.ulf + forward * vv.nearToFar * .25 ); //XXX ??? XXX //XXX this algorithm should be replaced. Perhaps instead of using //XXX 'forward' we could go a little up and to the left? //XXX ??? XXX if (data->type.isDerivedFrom(SoDirectionalLight::getClassTypeId())) { SoDirectionalLight *myLight = (SoDirectionalLight *) data->light; // the position of the light can't be given to the light itself. // So we use the translation and translation inverse to // get it to go where we want. data->translation->translation = position; data->translationInverse->translation = -position; myLight->direction = center - position; } else { // The data->scale will influence the position we set. // So we need to prepare for this. Note, it's not a prolem for // directional lights since they use the translation node, // which is outside the scale and scaleInverse grouping SbVec3f invrs = data->scaleInverse->scaleFactor.getValue(); SbVec3f scaledLoc = position; scaledLoc *= invrs[0]; if (data->type.isDerivedFrom(SoPointLight::getClassTypeId())) { SoPointLight *myLight = (SoPointLight *) data->light; myLight->location = scaledLoc; // no direction for this light } else if (data->type.isDerivedFrom(SoSpotLight::getClassTypeId())) { SoSpotLight *myLight = (SoSpotLight *) data->light; myLight->location = scaledLoc; myLight->direction = center - position; } } } //////////////////////////////////////////////////////////////////////// // // Description: // Creates and append the light data struct, and adds a menu entry // for the light. // // Use: private // SvLightData * SoSceneViewer::addLightEntry(SoLight *light, SoSwitch *lightSwitch) // //////////////////////////////////////////////////////////////////////// { // // create the light data // SvLightData *data = new SvLightData; lightDataList.append(data); light->ref(); data->light = light; data->lightSwitch = lightSwitch; // Try and find the scale, scaleInverse, translation, and // translationInverse. data->scale = NULL; data->scaleInverse = NULL; data->translation = NULL; data->translationInverse = NULL; SbBool gotLight = FALSE; for ( int i = 0; i < lightSwitch->getNumChildren(); i++ ) { SoNode *n = lightSwitch->getChild(i); if (n == light) gotLight = TRUE; else if (n->isOfType(SoScale::getClassTypeId())){ if (data->scale == NULL && gotLight == FALSE) data->scale = (SoScale *) n; else if (data->scaleInverse == NULL && gotLight == TRUE) data->scaleInverse = (SoScale *) n; } else if (n->isOfType(SoTranslation::getClassTypeId())){ if (data->translation == NULL && gotLight == FALSE) data->translation = (SoTranslation *) n; else if (data->translationInverse == NULL && gotLight == TRUE) data->translationInverse = (SoTranslation *) n; } } // Now install any missing nodes... if (data->scale == NULL) { data->scale = new SoScale; int lightInd = lightSwitch->findChild(light); lightSwitch->insertChild( data->scale, lightInd ); } if (data->scaleInverse == NULL) { data->scaleInverse = new SoScale; int lightInd = lightSwitch->findChild(light); lightSwitch->insertChild( data->scaleInverse, lightInd + 1 ); } if (data->translation == NULL) { data->translation = new SoTranslation; int scaleInd = lightSwitch->findChild(data->scale); lightSwitch->insertChild( data->translation, scaleInd ); } if (data->translationInverse == NULL) { data->translationInverse = new SoTranslation; int scaleInvInd = lightSwitch->findChild(data->scaleInverse); lightSwitch->insertChild( data->translationInverse, scaleInvInd+1 ); } // See if the size was already calculated (this happens when we read // .env files)... SbVec3f oldScale = data->scale->scaleFactor.getValue(); if ( calculatedLightManipSize == FALSE && oldScale != SbVec3f(1,1,1) ) { lightManipSize = oldScale[0]; calculatedLightManipSize = TRUE; } data->classPt = this; data->colorEditor = NULL; data->type = light->getTypeId(); // set the correct label name char *str; if (data->type.isDerivedFrom( SoDirectionalLight::getClassTypeId())) str = Light.directionallight; else if (data->type.isDerivedFrom( SoPointLight::getClassTypeId()) ) str = Light.pointlight; else if (data->type.isDerivedFrom( SoSpotLight::getClassTypeId()) ) str = Light.spotlight; else str = "??? "; data->name = strdup(str); // Set the size for the light manips. if ( !calculatedLightManipSize ) calculateLightManipSize(); data->scale->scaleFactor.setValue( lightManipSize, lightManipSize, lightManipSize ); float invSz = (lightManipSize == 0.0) ? 1.0 : 1.0 / lightManipSize; data->scaleInverse->scaleFactor.setValue( invSz, invSz, invSz ); // // add the menu entry // addLightMenuEntry(data); return data; } //////////////////////////////////////////////////////////////////////// // // Description: // build the light menu entry for the given light. // // Use: private // void SoSceneViewer::addLightMenuEntry(SvLightData *data) // //////////////////////////////////////////////////////////////////////// { // // create the motif menu entry // Widget menu = menuItems[SV_LIGHT].widget; // makes sure menu has been built if (menu == NULL) return; // create the submenu widget, adding a callback to update the toggles Arg args[8]; int argnum = 0; #ifdef MENUS_IN_POPUP SoXt::getPopupArgs(XtDisplay(menu), SCREEN(menu), args, &argnum); #endif data->submenuWidget = XmCreatePulldownMenu(menu, "submenu", args, argnum); XtAddCallback(data->submenuWidget, XmNmapCallback, (XtCallbackProc) SoSceneViewer::lightSubmenuDisplay, (XtPointer) data); // create a cascade menu entry which will bring the submenu XtSetArg(args[0], XmNsubMenuId, data->submenuWidget); data->cascadeWidget = XtCreateWidget(data->name, xmCascadeButtonGadgetClass, menu, args, 1); // add "on/off" toggle data->onOffWidget = XtCreateWidget("On/Off", xmToggleButtonGadgetClass, data->submenuWidget, NULL, 0); XtAddCallback(data->onOffWidget, XmNvalueChangedCallback, (XtCallbackProc) SoSceneViewer::lightToggleCB, (XtPointer) data); // add "Icon" toggle data->iconWidget = XtCreateWidget("Icon", xmToggleButtonGadgetClass, data->submenuWidget, NULL, 0); XtAddCallback(data->iconWidget, XmNvalueChangedCallback, (XtCallbackProc) SoSceneViewer::editLightToggleCB, (XtPointer) data); // add "Edit Color" toggle data->editColorWidget = XtCreateWidget("Edit Color", xmPushButtonGadgetClass, data->submenuWidget, NULL, 0); XtAddCallback(data->editColorWidget, XmNactivateCallback, (XtCallbackProc) SoSceneViewer::editLightColorCB, (XtPointer) data); // add "Remove" entry data->removeWidget = XtCreateWidget("Remove", xmPushButtonGadgetClass, data->submenuWidget, NULL, 0); XtAddCallback(data->removeWidget, XmNactivateCallback, (XtCallbackProc) SoSceneViewer::removeLightCB, (XtPointer) data); // manage children XtManageChild(data->onOffWidget); XtManageChild(data->iconWidget); XtManageChild(data->editColorWidget); XtManageChild(data->removeWidget); XtManageChild(data->cascadeWidget); } //////////////////////////////////////////////////////////////////////// // // Description: // Called by "On/Off" light menu entry when toggle changes. // // Use: static private // void SoSceneViewer::lightToggleCB(Widget toggle, SvLightData *data, void *) // //////////////////////////////////////////////////////////////////////// { data->classPt->turnLightOnOff(data, XmToggleButtonGetState(toggle)); } //////////////////////////////////////////////////////////////////////// // // Description: // Turn the given light on or off. // // Use: private // void SoSceneViewer::turnLightOnOff(SvLightData *data, SbBool flag) // //////////////////////////////////////////////////////////////////////// { // check if it is the headlight if (data == headlightData) setHeadlight( flag ); else { if ( flag ) SWITCH_LIGHT_ON(data->lightSwitch); else SWITCH_LIGHT_OFF(data->lightSwitch); } } //////////////////////////////////////////////////////////////////////// // // Description: // "Edit" light menu entry callback. // // Use: static private // void SoSceneViewer::editLightToggleCB(Widget toggle, SvLightData *data, void *) // //////////////////////////////////////////////////////////////////////// { data->classPt->editLight( data, XmToggleButtonGetState(toggle) ); } //////////////////////////////////////////////////////////////////////// // // Description: // Attach/detach the correct manipulator on the given light. // // Use: private // void SoSceneViewer::editLight(SvLightData *data, SbBool flag) // //////////////////////////////////////////////////////////////////////// { // ??? check if this is for the headlight, which is special cased // ??? since a manipulator cannot be used (aligned to camera). SbBool forHeadlight = (data == data->classPt->headlightData); SoType dataType = data->type; SoType dirLightType = SoDirectionalLight::getClassTypeId(); SoType ptLightType = SoPointLight::getClassTypeId(); SoType spotLightType = SoSpotLight::getClassTypeId(); // // attach the manip to the light and add it to the scene // if (flag) { if (forHeadlight) { if (headlightEditor == NULL) { headlightEditor = new SoXtDirectionalLightEditor; headlightEditor->setTitle("Headlight Editor"); } // Make sure we have the current viewer's headlight SoLight *l = data->classPt->getHeadlight(); l->ref(); if (data->light) data->light->unref(); data->light = l; // attach the dir light editor // ??? don't use the path from the root to the headlight // ??? since we want the light to be relative to the // ??? camera (i.e. moving the camera shouldn't affect // ??? the arrow in the editor since that direction // ??? is relative to the camera). SoPath *littlePath = new SoPath( data->light ); headlightEditor->attach( littlePath ); headlightEditor->show(); } else if (data->isManip() == FALSE) { // NOTE: if isManip() == TRUE, then the light is already a manip // and doesn't need to be changed. SoLight *newManip = NULL; // allocate the right manipulator type if needed if ( dataType.isDerivedFrom( dirLightType ) ) { newManip = new SoDirectionalLightManip; newManip->ref(); } else if ( dataType.isDerivedFrom( ptLightType ) ) { newManip = new SoPointLightManip; newManip->ref(); } else if ( dataType.isDerivedFrom( spotLightType ) ) { newManip = new SoSpotLightManip; newManip->ref(); // Set dropOffRate non-zero, or it will look like a pointLight. ((SoSpotLightManip *)newManip)->dropOffRate = .01; } // get the path from the root to the light node SoSearchAction sa; sa.setNode( data->light ); sa.apply( currentViewer->getSceneGraph() ); SoPath *path = sa.getPath(); // ??? light is probably turned off so we don't // ??? need to print a warning message. Just don't // ??? do anything if (path == NULL) { newManip->unref(); return; } path->ref(); // Set the size for the light manips. if ( !calculatedLightManipSize ) calculateLightManipSize(); data->scale->scaleFactor.setValue( lightManipSize, lightManipSize, lightManipSize ); float invSz = (lightManipSize == 0.0) ? 1.0 : 1.0 / lightManipSize; data->scaleInverse->scaleFactor.setValue( invSz, invSz, invSz ); // Put the manip into the scene. if ( dataType.isDerivedFrom( ptLightType ) ) ((SoPointLightManip *)newManip)->replaceNode(path); else if (dataType.isDerivedFrom(dirLightType) ) ((SoDirectionalLightManip *)newManip)->replaceNode(path); else if (dataType.isDerivedFrom(spotLightType)) ((SoSpotLightManip *)newManip)->replaceNode(path); // Okay, now that we stuck that manip in there, // we better make a note of it... path->unref(); data->light->unref(); data->light = newManip; } } // // detach the manip from the light and remove it from the scene // else { if (forHeadlight) { // detach editor from light if (headlightEditor != NULL) { headlightEditor->detach(); headlightEditor->hide(); } } else if (data->isManip() == TRUE ) { // replace the lightManip node with a regular light node // get the path from the root to the lightManip node SoSearchAction sa; sa.setNode( data->light ); sa.apply( currentViewer->getSceneGraph() ); SoPath *path = sa.getPath(); if (path != NULL ) { path->ref(); SoLight *newLight; if (dataType.isDerivedFrom(ptLightType)) { newLight = new SoPointLight; newLight->ref(); ((SoPointLightManip *)data->light)->replaceManip( path, (SoPointLight *)newLight ); } else if (dataType.isDerivedFrom(dirLightType)) { // when removing a directional light manip, // we don't want to lose the translation of the manip. // Since the regular light can't hold this value, // get it from the manip and move it to the translation. transferDirectionalLightLocation(data); newLight = new SoDirectionalLight; newLight->ref(); ((SoDirectionalLightManip *)data->light)->replaceManip( path, (SoDirectionalLight *)newLight ); } else if (dataType.isDerivedFrom(spotLightType)) { newLight = new SoSpotLight; newLight->ref(); ((SoSpotLightManip *)data->light)->replaceManip( path, (SoSpotLight *)newLight ); } path->unref(); data->light->unref(); data->light = newLight; } } } } //////////////////////////////////////////////////////////////////////// // // Description: // When removing or writing directional light manip, // we don't want to lose the translation of the manip. // Since the regular light can't hold this value, // get it from the manip and move it to the translation. // // Use: private // void SoSceneViewer::transferDirectionalLightLocation( SvLightData *data ) // //////////////////////////////////////////////////////////////////////// { if ( !data->light ) return; if ( !data->light->isOfType(SoDirectionalLightManip::getClassTypeId() )) return; // when removing a directional light manip, // we don't want to lose the translation of the manip. // Since the regular light can't hold this value, // get it from the manip and move it to the translation. SoDirectionalLightManip *manip = (SoDirectionalLightManip *) data->light; SoDirectionalLightDragger *dragger = (SoDirectionalLightDragger *) manip->getDragger(); SbVec3f lightTrans(0,0,0); if (dragger) lightTrans += dragger->translation.getValue(); SbVec3f scl = data->scale->scaleFactor.getValue(); lightTrans *= scl[0]; lightTrans += data->translation->translation.getValue(); data->translation->translation = lightTrans; data->translationInverse->translation = -lightTrans; // Now zero out the translation in the dragger itself: dragger->translation = SbVec3f(0,0,0); } //////////////////////////////////////////////////////////////////////// // // Description: // Called by "Edit Color" light menu entry. // // Use: static private // void SoSceneViewer::editLightColorCB(Widget, SvLightData *data, void *) // //////////////////////////////////////////////////////////////////////// { // create the color editor with the right title if (data->colorEditor == NULL) { data->colorEditor = new MyColorEditor; char str[50]; strcpy(str, data->name); strcat(str, " Light Color"); data->colorEditor->setTitle(str); } if ( !data->colorEditor->isAttached() ) { // if this is for the headlight, make sure we have the // current viewer headlight if (data == data->classPt->headlightData) { SoLight *l = data->classPt->getHeadlight(); l->ref(); if (data->light) data->light->unref(); data->light = l; } // normalize the light intensity SbColor col; col = data->light->color.getValue(); col *= data->light->intensity.getValue(); data->light->intensity.setValue( 1.0 ); data->light->color.setValue( col ); data->colorEditor->attach( &data->light->color, data->light ); } data->colorEditor->show(); } //////////////////////////////////////////////////////////////////////// // // Description: // remove button menu entry callback. // // Use: static private // void SoSceneViewer::removeLightCB(Widget, SvLightData *data, void *) // //////////////////////////////////////////////////////////////////////// { data->classPt->removeLight(data); } //////////////////////////////////////////////////////////////////////// // // Description: // removes the light from the scene, and removes the light data // and pulldown menu entry. // // Use: private // void SoSceneViewer::removeLight(SvLightData *data) // //////////////////////////////////////////////////////////////////////// { // delete the color editor and manip delete data->colorEditor; // note: deleted code that dealt with the manip. // Since the light and the manip are one and the same now. // unrefing the light also removes the manip // unref the light (or manip) for this entry if (data->light) data->light->unref(); // remove the light from the scene lightGroup->removeChild( data->lightSwitch ); // nuke the menu entry if (data->cascadeWidget != NULL) XtDestroyWidget( data->cascadeWidget ); // remove from list and delete the struct lightDataList.remove( lightDataList.find(data) ); delete data; } //////////////////////////////////////////////////////////////////////// // // Description: // Calculate the size for the light manips. // Called when the first lightmanip is created. // Also called when a scene is read or imported, assuming lights // have already been created. // // Use: private // void SoSceneViewer::calculateLightManipSize() // //////////////////////////////////////////////////////////////////////// { // Set the size for the light manips. // Calculate a good size, based on the size of the scene. // Once this size is determined, use it for all other light manips. // (We need to save the value because the scene size will change // over time, but we want all light manips to be the same size. // This routine will also get called again if a new scene is read in. // Run a bounding box action on the scene... SoGetBoundingBoxAction ba(currentViewer->getViewportRegion()); ba.apply( currentViewer->getSceneGraph() ); SbBox3f sceneBox = ba.getBoundingBox(); SbVec3f size; if (sceneBox.isEmpty()) { // Oh, just take a guess. Default scene would be 2x2x2 size.setValue(2,2,2); } else size = sceneBox.getMax() - sceneBox.getMin(); //XXX pick a good size! lightManipSize = .025 * size.length(); // Visit every light entry and set the scale and scaleInverse // nodes to have the correct lightManipSize. for (int i = 0; i < lightDataList.getLength(); i++ ) { SvLightData *data = (SvLightData *) lightDataList[i]; if (data->scale != NULL) { if (data->scale->scaleFactor.getValue()[0] != lightManipSize) data->scale->scaleFactor.setValue( lightManipSize, lightManipSize, lightManipSize ); } if (data->scaleInverse != NULL) { float invSz = (lightManipSize == 0.0) ? 1.0 : 1.0 / lightManipSize; if (data->scaleInverse->scaleFactor.getValue()[0] != invSz) data->scaleInverse->scaleFactor.setValue( invSz, invSz, invSz ); } } calculatedLightManipSize = TRUE; } //////////////////////////////////////////////////////////////////////// // // Description: // Called whenever a light submenu is mapped on screen (update // the toggles) // // Use: static private // void SoSceneViewer::lightSubmenuDisplay(Widget, SvLightData *data, void *) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = data->classPt; SbBool set; // // update the "on/off" toggle // if (data == sv->headlightData) set = sv->isHeadlight(); else set = IS_LIGHT_ON(data->lightSwitch); if (set) TOGGLE_ON(data->onOffWidget); else TOGGLE_OFF(data->onOffWidget); // // update the "Edit" toggle // if (data == sv->headlightData) set = (sv->headlightEditor != NULL && sv->headlightEditor->isVisible()); else set = (data->isManip() == TRUE ); if (set) TOGGLE_ON(data->iconWidget); else TOGGLE_OFF(data->iconWidget); } //////////////////////////////////////////////////////////////////////// // // Description: // Called after a paste operation has completed. // // Use: static, private // void SoSceneViewer::pasteDoneCB(void *userData, SoPathList *pathList) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; sv->pasteDone(pathList); } //////////////////////////////////////////////////////////////////////// // // Description: // Called after a drop operation has completed. // // Use: static, private // SbBool SoSceneViewer::iconDropCB(void *userData, const char *filename) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; sv->fileMode = SV_FILE_IMPORT; return sv->doFileIO(filename); } //////////////////////////////////////////////////////////////////////// // // Description: // Called during a drop operation of INVENTOR objects. // // Use: static, private // SbBool SoSceneViewer::objDropCB( void *userData, Atom target, void *data, uint32_t numBytes) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; SoPathList *pathList = NULL; SbBool success = FALSE; Display *d = SoXt::getDisplay(); if (data != NULL) { if (target == _ATOM_(d, "INVENTOR_2_1") || target == _ATOM_(d, "VRML_1_0") || target == _ATOM_(d, "INVENTOR") || target == _ATOM_(d, "INVENTOR_2_0")) { pathList = SoByteStream::unconvert(data, numBytes); if (pathList != NULL && pathList->getLength() > 0) { sv->pasteDone(pathList); success = TRUE; // pathList will be deleted by pasteDone... } } } return success; } //////////////////////////////////////////////////////////////////////// // // Description: // Called after a paste operation has completed, this adds the // pasted data to our scene graph. // // Use: private // void SoSceneViewer::pasteDone(SoPathList *pathList) // //////////////////////////////////////////////////////////////////////// { if (pathList->getLength() <= 0) return; // If this is the only obj, we will view all SbBool doViewAll = (selection->getNumChildren() == 0); // first, detach manips from all selected objects detachManipFromAll(); // now, turn off the sel/desel callbacks. // we'll turn them on again after we've adjusted the selection selection->removeSelectionCallback( SoSceneViewer::selectionCallback, this ); selection->removeDeselectionCallback( SoSceneViewer::deselectionCallback, this ); // now deselect all, and build up a selection from the pasted paths selection->deselectAll(); // Add every path in the path list as a child under selection. // Then select each of these paths. for (int i = 0; i < pathList->getLength(); i++) { // if the head of the path is a selection node, then don't // paste the head - rather, paste all of its children. // this makes sure we don't have more than 1 selection node. // While we're adding the paths as children, select each path. SoPath *p = (*pathList)[i]; SoNode *head = p->getHead(); SoPath *selpath; if (head->isOfType(SoSelection::getClassTypeId())) { for (int j = 0; j < ((SoSelection *)head)->getNumChildren(); j++) { selection->addChild(((SoSelection *)head)->getChild(j)); // create a path from selection to this child // and select the path. selpath = new SoPath(selection); selpath->append(selection->getNumChildren() - 1); selection->select(selpath); } } else { // not a selection node, so just add it. selection->addChild(p->getHead()); // create a path from selection to this child // and select the path. selpath = new SoPath(selection); selpath->append(selection->getNumChildren() - 1); selection->select(selpath); } } // now add manips to all the selected objects attachManipToAll(curManip); // and turn the sel/desel callbacks back on selection->addSelectionCallback( SoSceneViewer::selectionCallback, this ); selection->addDeselectionCallback( SoSceneViewer::deselectionCallback, this ); // enable/disable keyboard shortcuts updateCommandAvailability(); // View all? if (doViewAll) viewAll(); delete pathList; } //////////////////////////////////////////////////////////////////////// // // Description: // Build routine for SceneViewer. This creates all of the X widgets // // Use: public, virtual Widget SoSceneViewer::buildWidget(Widget parent) // //////////////////////////////////////////////////////////////////////// { int n; Arg args[10]; // create a form to hold everything together SbVec2s size = getSize(); n = 0; if (size[0] != 0 && size[1] != 0) { XtSetArg(args[n], XtNwidth, size[0]); n++; XtSetArg(args[n], XtNheight, size[1]); n++; } mgrWidget = XtCreateWidget(getWidgetName(), xmFormWidgetClass, parent, args, n); registerWidget(mgrWidget); // create the topbar menu if (showMenuFlag) buildAndLayoutMenu(mgrWidget); // build and layout the current viewer whichViewer = SV_VWR_EXAMINER; setTitle("SceneViewer (Examiner)"); currentViewer = viewerList[whichViewer] = new SoXtExaminerViewer(mgrWidget); currentViewer->setSceneGraph(sceneGraph); currentViewer->setGLRenderAction(highlightRA); currentViewer->redrawOnSelectionChange(selection); setOverlayLogo(currentViewer); // Fog environment->fogColor.setValue( currentViewer->getBackgroundColor() ); // since we created the camera, do a view all and save this // as the starting point (don't want default camera values). viewAll(); saveHomePosition(); buildAndLayoutViewer(currentViewer); // manage those children if (showMenuFlag) XtManageChild(menuWidget); currentViewer->show(); // clipboard is for copy/paste of 3d data. //??? what if this SceneViewer had its widget destroyed and rebuilt? //??? we need to destroy the clipboards when that happens. clipboard = new SoXtClipboard(mgrWidget); MyDropSite::registerCallback( mgrWidget, iconDropCB, objDropCB, this); return mgrWidget; } //////////////////////////////////////////////////////////////////////// // // Description: // Builds and layout the given viewer. // // Use: private void SoSceneViewer::buildAndLayoutViewer(SoXtFullViewer *vwr) // //////////////////////////////////////////////////////////////////////// { if (mgrWidget == NULL) return; // build the viewer if necessary if (vwr->getWidget() == NULL) { fprintf(stderr, "ERROR - need to create the viewer widget before it can be layed out\n"); return; } // layout the viewer to be attached under the topbar menu // (if the pulldown menu is shown) Arg args[12]; int n = 0; if ( showMenuFlag ) { XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNtopWidget, menuWidget); n++; } else { XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; } XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetValues(vwr->getWidget(), args, n); } //////////////////////////////////////////////////////////////////////// // // Description: // Create topbar menu. Invalid buttons are rendered gray. // Each button's callback include a structure with the ID // of the button and a pointer to the SoSceneViewer that created // it. // // Use: private // void SoSceneViewer::buildAndLayoutMenu(Widget parent) // //////////////////////////////////////////////////////////////////////// { if (menuWidget != NULL) return; Arg args[8]; int i, j, n, id; WidgetList buttons, subButtons; int itemCount, subItemCount; WidgetClass widgetClass; String callbackReason; XmString xmstr; // create topbar menu menuWidget = XmCreateMenuBar(parent, "menuBar", NULL, 0); Arg popupargs[4]; int popupn = 0; #ifdef MENUS_IN_POPUP Widget shell = SoXt::getShellWidget(mgrWidget); SoXt::getPopupArgs(XtDisplay(menuWidget), SCREEN(shell), popupargs, &popupn); #endif itemCount = XtNumber(pulldownData); buttons = (WidgetList) XtMalloc(itemCount * sizeof(Widget)); for (i = 0; i < itemCount; i++) { // Make Topbar menu button Widget subMenu = XmCreatePulldownMenu(menuWidget, "subMenu", popupargs, popupn); #ifdef MENUS_IN_POPUP // register callbacks to load/unload the pulldown colormap when the // pulldown menu is posted. SoXt::registerColormapLoad(subMenu, shell); #endif id = pulldownData[i].id; menuItems[id].widget = subMenu; XtAddCallback(subMenu, XmNmapCallback, (XtCallbackProc) SoSceneViewer::menuDisplay, (XtPointer) &menuItems[id]); XtSetArg(args[0], XmNsubMenuId, subMenu); buttons[i] = XtCreateWidget(pulldownData[i].name, xmCascadeButtonGadgetClass, menuWidget, args, 1); // Make subMenu buttons subItemCount = pulldownData[i].subItemCount; subButtons = (WidgetList) XtMalloc(subItemCount * sizeof(Widget)); for (j = 0; j < subItemCount; j++) { if (pulldownData[i].subMenu[j].buttonType == SV_SEPARATOR) subButtons[j] = XtCreateWidget(NULL, xmSeparatorGadgetClass, subMenu, NULL, 0); else { switch (pulldownData[i].subMenu[j].buttonType) { case SV_PUSH_BUTTON: widgetClass = xmPushButtonGadgetClass; callbackReason = XmNactivateCallback; n = 0; break; case SV_TOGGLE_BUTTON: widgetClass = xmToggleButtonGadgetClass; callbackReason = XmNvalueChangedCallback; n = 0; break; case SV_RADIO_BUTTON: widgetClass = xmToggleButtonGadgetClass; callbackReason = XmNvalueChangedCallback; XtSetArg(args[0], XmNindicatorType, XmONE_OF_MANY); n = 1; break; default: fprintf(stderr, "SceneViewer INTERNAL ERROR: bad buttonType\n"); break; } // check for keyboard accelerator char *accel = pulldownData[i].subMenu[j].accelerator; char *accelText = pulldownData[i].subMenu[j].accelText; xmstr = NULL; if (accel != NULL) { XtSetArg(args[n], XmNaccelerator, accel); n++; if (accelText != NULL) { xmstr = XmStringCreate(accelText, XmSTRING_DEFAULT_CHARSET); XtSetArg(args[n], XmNacceleratorText, xmstr); n++; } } subButtons[j] = XtCreateWidget( pulldownData[i].subMenu[j].name, widgetClass, subMenu, args, n); if (xmstr != NULL) XmStringFree(xmstr); id = pulldownData[i].subMenu[j].id; menuItems[id].widget = subButtons[j]; XtAddCallback(subButtons[j], callbackReason, (XtCallbackProc)SoSceneViewer::processTopbarEvent, (XtPointer) &menuItems[id]); } } XtManageChildren(subButtons, subItemCount); XtFree((char *)subButtons); } XtManageChildren(buttons, itemCount); XtFree((char *)buttons); // // layout the menu bar // n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetValues(menuWidget, args, n); // // Add the light items which are dynamically created // // first add the headlight addLightMenuEntry(headlightData); XtUnmanageChild(headlightData->removeWidget); XtUnmanageChild(headlightData->editColorWidget); xmstr = XmStringCreate( Light.editlight, XmSTRING_DEFAULT_CHARSET); XtSetArg(args[0], XmNlabelString, xmstr); XtSetValues(headlightData->iconWidget, args, 1); XmStringFree(xmstr); // now the regular lights for (i=0; i < lightDataList.getLength(); i++) addLightMenuEntry( (SvLightData *) lightDataList[i] ); } //////////////////////////////////////////////////////////////////////// // // Description: // Show/hide the pulldown menu bar. // // Use: public void SoSceneViewer::showMenu(SbBool flag) // //////////////////////////////////////////////////////////////////////// { if (showMenuFlag == flag || mgrWidget == NULL) { showMenuFlag = flag; return; } showMenuFlag = flag; if ( showMenuFlag ) { // turn topbar menu on if (menuWidget == NULL) buildAndLayoutMenu(mgrWidget); XtManageChild(menuWidget); // attach viewer to bottom of menu Arg args[2]; int n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNtopWidget, menuWidget); n++; XtSetValues(currentViewer->getWidget(), args, n); } else { // attach viewer to form Arg args[2]; int n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetValues(currentViewer->getWidget(), args, n); // turn topbar menu off if (menuWidget != NULL) XtUnmanageChild(menuWidget); } } //////////////////////////////////////////////////////////////////////// // // Manage the changes in the selected node(s) // // Use: private, static // SoPath * SoSceneViewer::pickFilterCB(void *userData, const SoPickedPoint *pick) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; SoPath *filteredPath = NULL; // If there are any transform manips along the path, check if they // belong to our personal set of manips. // If so, change the path so it points to the object the manip // is attached to. SoFullPath *fullP = (SoFullPath *) pick->getPath(); SoNode *n; for (int i = 0; i < fullP->getLength(); i++ ) { n = fullP->getNode(i); if (n->isOfType(SoTransformManip::getClassTypeId())) { int which = sv->maniplist->find((SoTransformManip *) n); if (which != -1) { filteredPath = sv->maniplist->getSelectionPath(which); return filteredPath; } } } // If we didn't pick one of our manipulators, then return the pickPath filteredPath = pick->getPath(); return filteredPath; } //////////////////////////////////////////////////////////////////////// // // Manage the changes in the selected node(s) // // Use: private, static // void SoSceneViewer::deselectionCallback( void *userData, // my data SoPath *deselectedPath ) // object // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; // remove the manip sv->detachManip( deselectedPath ); // Remove editors if (sv->materialEditor) sv->materialEditor->detach(); if (sv->colorEditor) sv->colorEditor->detach(); if (sv->transformSliderSet) sv->transformSliderSet->setNode( NULL ); // enable/disable cmd key shortcuts and menu items sv->updateCommandAvailability(); } //////////////////////////////////////////////////////////////////////// // // Manage the changes in the selected node(s) // // Use: private, static // void SoSceneViewer::selectionCallback( void *userData, // my data SoPath *selectedPath ) // object // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = (SoSceneViewer *) userData; // attach the manip sv->attachManip( sv->curManip, selectedPath ); // // If active, attach editors to new selection. // SoMaterial *mtl = NULL; if ( sv->materialEditor && sv->materialEditor->isVisible()) { mtl = sv->findMaterialForAttach(selectedPath); sv->materialEditor->attach(mtl); } if (sv->colorEditor && sv->colorEditor->isVisible()) { if (mtl == NULL) mtl = sv->findMaterialForAttach(selectedPath); sv->colorEditor->attach(&(mtl->diffuseColor), 0, mtl); } if ( sv->transformSliderSet && sv->transformSliderSet->isVisible() ) { SoPath *editTransformPath; editTransformPath = sv->findTransformForAttach( selectedPath ); if ( editTransformPath == NULL ) { sv->transformSliderSet->setNode( NULL ); } else { editTransformPath->ref(); sv->transformSliderSet->setNode(((SoFullPath *)editTransformPath)->getTail() ); editTransformPath->unref(); } } // enable/disable cmd key shortcuts and menu items sv->updateCommandAvailability(); } //////////////////////////////////////////////////////////////////////// // // Remove selected objects from the scene graph. // In this demo, we don't really know how the graphs are set up, // so act conservatively, and simply remove the node which is the // tail of the path from its parent. Note if the node is instanced, // all instances will be destroyed. Then travel up the path to a // parent separator. If there are no other shapes under the separator, // destroy it too. // // Other applications might delete selected objects a different way, // depending on how the data is organized in the scene graph. // // Use: protected // void SoSceneViewer::destroySelectedObjects() // //////////////////////////////////////////////////////////////////////// { for (int i = selection->getNumSelected() - 1; i >= 0; i--) { SoPath *p = (*selection)[i]; p->ref(); // Deselect this path selection->deselect(i); // Remove the tail node from the graph SoGroup *g = (SoGroup *) p->getNodeFromTail(1); g->removeChild(p->getTail()); // Travel up the path to separators, and see if this was // the only shape node under the sep. If so, delete the sep too. // (Don't go all the way up to the selection node). SbBool shapeFound = FALSE; int j = 0; while ((! shapeFound) && (j < p->getLength() - 1)) { SoNode *n = p->getNodeFromTail(j); if (n->isOfType(SoSeparator::getClassTypeId())) { // Search for other shape nodes SoSearchAction sa; sa.setFind(SoSearchAction::TYPE); sa.setType(SoShape::getClassTypeId()); sa.apply(n); // If no other shapes under this separator, delete it! if (sa.getPath() == NULL) { g = (SoGroup *) p->getNodeFromTail(j + 1); g->removeChild(n); // Reset j since we have a new end of path j = 0; } else shapeFound = TRUE; } // Else a group with no children? else if (n->isOfType(SoGroup::getClassTypeId()) && (((SoGroup *)n)->getNumChildren() == 0)) { g = (SoGroup *) p->getNodeFromTail(j + 1); g->removeChild(n); // Reset j since we have a new end of path j = 0; } // Else continue up the path looking for separators else j++; } p->unref(); } } //////////////////////////////////////////////////////////////////////// // // This enables/disables cmd key shortcuts and menu items // based on whether there are any objects, and/or any selected objects // in the scene graph. // // Use: static private // // void SoSceneViewer::updateCommandAvailability() // //////////////////////////////////////////////////////////////////////// { Arg args[1]; // enable/disable based on the number of child objects in scene if (selection->getNumChildren() == 0) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); // save (if no children, nothing to save) XtSetValues(menuItems[SV_FILE_SAVE].widget, args, 1); XtSetValues(menuItems[SV_FILE_SAVE_AS].widget, args, 1); // pickAll (if no children, nothing to pick) XtSetValues(menuItems[SV_EDIT_PICK_ALL].widget, args, 1); // enable/disable based on the number of selected objects if (selection->getNumSelected() == 0) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); // if nothing selected, then cannot pick parent, cut, copy, delete, // view selection, bring up editors XtSetValues(menuItems[SV_EDIT_PICK_PARENT].widget, args, 1); XtSetValues(menuItems[SV_EDIT_CUT].widget, args, 1); XtSetValues(menuItems[SV_EDIT_COPY].widget, args, 1); XtSetValues(menuItems[SV_EDIT_DELETE].widget, args, 1); XtSetValues(menuItems[SV_VIEW_SELECTION].widget, args, 1); XtSetValues(menuItems[SV_EDITOR_TRANSFORM].widget, args, 1); XtSetValues(menuItems[SV_EDITOR_MATERIAL].widget, args, 1); XtSetValues(menuItems[SV_EDITOR_COLOR].widget, args, 1); } //////////////////////////////////////////////////////////////////////// // // Called by Xt when a menu is about to be displayed. // This gives us a chance to update any items in the menu. // // Use: static private // void SoSceneViewer::menuDisplay(Widget, SoSceneViewerData *data, XtPointer) // //////////////////////////////////////////////////////////////////////// { SoSceneViewer *sv = data->classPt; Arg args[1]; char str[100]; XmString xmstr; switch (data->id) { case SV_FILE: // disable saving if there isn't any geometry if (sv->selection->getNumChildren() == 0) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); XtSetValues(sv->menuItems[SV_FILE_SAVE].widget, args, 1); XtSetValues(sv->menuItems[SV_FILE_SAVE_AS].widget, args, 1); // update the "Save" menu entry to reflect the current file name #ifndef EXPLORER strcpy(str,fileData[5].name ); #else strcpy(str,fileData[0].name ); #endif if (sv->fileName != NULL) { // get the file name withought the entire path char *pt = strrchr(sv->fileName, '/'); // last occurance of '/' pt = (pt == NULL) ? sv->fileName : pt + 1; strcat(str, " -> "); strcat(str, pt); } xmstr = XmStringCreate(str, XmSTRING_DEFAULT_CHARSET); XtSetArg(args[0], XmNlabelString, xmstr); XtSetValues(sv->menuItems[SV_FILE_SAVE].widget, args, 1); XmStringFree(xmstr); break; #ifndef EXPLORER case SV_EDIT: // disable cut, copy, delete, pickParent if there is no selection if(sv->selection->getNumSelected() == 0) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); XtSetValues(sv->menuItems[SV_EDIT_PICK_PARENT].widget, args, 1); XtSetValues(sv->menuItems[SV_EDIT_CUT].widget, args, 1); XtSetValues(sv->menuItems[SV_EDIT_COPY].widget, args, 1); XtSetValues(sv->menuItems[SV_EDIT_DELETE].widget, args, 1); // disable pick all if there are nothing to pick if (sv->selection->getNumChildren() == 0) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); XtSetValues(sv->menuItems[SV_EDIT_PICK_ALL].widget, args, 1); break; #endif /* EXPLORER */ case SV_VIEW: // set pick/edit toggle if ( sv->isViewing() ) TOGGLE_OFF(sv->menuItems[SV_VIEW_PICK].widget); else TOGGLE_ON(sv->menuItems[SV_VIEW_PICK].widget); #ifdef EXPLORER // set user pick toggle if ( sv->userModeFlag ) TOGGLE_ON(sv->menuItems[SV_VIEW_USER].widget); else TOGGLE_OFF(sv->menuItems[SV_VIEW_USER].widget); #endif // Set the correct viewer TOGGLE_OFF(sv->menuItems[SV_VIEW_EXAMINER].widget); TOGGLE_OFF(sv->menuItems[SV_VIEW_WALK].widget); TOGGLE_OFF(sv->menuItems[SV_VIEW_PLANE].widget); TOGGLE_OFF(sv->menuItems[SV_VIEW_FLY].widget); switch ( sv->whichViewer ) { case SV_VWR_EXAMINER: TOGGLE_ON(sv->menuItems[SV_VIEW_EXAMINER].widget); break; case SV_VWR_WALK: TOGGLE_ON(sv->menuItems[SV_VIEW_WALK].widget); break; case SV_VWR_FLY: TOGGLE_ON(sv->menuItems[SV_VIEW_FLY].widget); break; case SV_VWR_PLANE: TOGGLE_ON(sv->menuItems[SV_VIEW_PLANE].widget); break; } // set the correct transparency type TOGGLE_OFF(sv->menuItems[SV_VIEW_SCREEN_TRANSPARENCY].widget); TOGGLE_OFF(sv->menuItems[SV_VIEW_BLEND_TRANSPARENCY].widget); TOGGLE_OFF(sv->menuItems[SV_VIEW_DELAY_BLEND_TRANSPARENCY].widget); TOGGLE_OFF(sv->menuItems[SV_VIEW_SORT_BLEND_TRANSPARENCY].widget); switch( sv->getTransparencyType() ) { case SoGLRenderAction::SCREEN_DOOR: TOGGLE_ON(sv->menuItems[SV_VIEW_SCREEN_TRANSPARENCY].widget); break; case SoGLRenderAction::BLEND: TOGGLE_ON(sv->menuItems[SV_VIEW_BLEND_TRANSPARENCY].widget); break; case SoGLRenderAction::DELAYED_BLEND: TOGGLE_ON(sv->menuItems[SV_VIEW_DELAY_BLEND_TRANSPARENCY].widget); break; case SoGLRenderAction::SORTED_OBJECT_BLEND: TOGGLE_ON(sv->menuItems[SV_VIEW_SORT_BLEND_TRANSPARENCY].widget); break; } // disable view selection if nothing is selected if ( sv->selection->getNumSelected() == 0 ) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); XtSetValues(sv->menuItems[SV_VIEW_SELECTION].widget, args, 1); // set fog toggle if ( sv->fogFlag ) TOGGLE_ON(sv->menuItems[SV_VIEW_FOG].widget); else TOGGLE_OFF(sv->menuItems[SV_VIEW_FOG].widget); // set antialiasing toggle if ( sv->antialiasingFlag ) TOGGLE_ON(sv->menuItems[SV_VIEW_ANTIALIASING].widget); else TOGGLE_OFF(sv->menuItems[SV_VIEW_ANTIALIASING].widget); break; #ifndef EXPLORER case SV_SELECTION: // mirror the selection policy TOGGLE_OFF(sv->menuItems[SV_SEL_SINGLE_SELECT].widget); TOGGLE_OFF(sv->menuItems[SV_SEL_TOGGLE_SELECT].widget); TOGGLE_OFF(sv->menuItems[SV_SEL_SHIFT_SELECT].widget); switch ( sv->selection->policy.getValue() ) { case SoSelection::SINGLE: TOGGLE_ON (sv->menuItems[SV_SEL_SINGLE_SELECT].widget); break; case SoSelection::TOGGLE: TOGGLE_ON (sv->menuItems[SV_SEL_TOGGLE_SELECT].widget); break; case SoSelection::SHIFT: TOGGLE_ON (sv->menuItems[SV_SEL_SHIFT_SELECT].widget); break; default: fprintf(stderr, "INTERNAL ERROR, unknown selection policy\n"); break; } break; #endif /* EXPLORER */ case SV_EDITOR: // disable items if there is no selection if(sv->selection->getNumSelected() == 0) XtSetArg(args[0], XmNsensitive, False); else XtSetArg(args[0], XmNsensitive, True); XtSetValues(sv->menuItems[SV_EDITOR_TRANSFORM].widget, args, 1); XtSetValues(sv->menuItems[SV_EDITOR_MATERIAL].widget, args, 1); XtSetValues(sv->menuItems[SV_EDITOR_COLOR].widget, args, 1); break; case SV_MANIP: // First, the section with the different types of manipulators. TOGGLE_OFF( sv->menuItems[SV_MANIP_HANDLEBOX].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_TRANSFORMER].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_TRACKBALL].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_JACK].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_CENTERBALL].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_XFBOX].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_TABBOX].widget ); TOGGLE_OFF( sv->menuItems[SV_MANIP_NONE].widget ); // Turn appropriate radio button on if (sv->curManip == SV_HANDLEBOX) TOGGLE_ON(sv->menuItems[SV_MANIP_HANDLEBOX].widget); else if (sv->curManip == SV_TRACKBALL) TOGGLE_ON(sv->menuItems[SV_MANIP_TRACKBALL].widget); else if (sv->curManip == SV_TRANSFORMER) TOGGLE_ON(sv->menuItems[SV_MANIP_TRANSFORMER].widget); else if (sv->curManip == SV_JACK) TOGGLE_ON(sv->menuItems[SV_MANIP_JACK].widget); else if (sv->curManip == SV_CENTERBALL) TOGGLE_ON(sv->menuItems[SV_MANIP_CENTERBALL].widget); else if (sv->curManip == SV_XFBOX) TOGGLE_ON(sv->menuItems[SV_MANIP_XFBOX].widget); else if (sv->curManip == SV_TABBOX) TOGGLE_ON(sv->menuItems[SV_MANIP_TABBOX].widget); else TOGGLE_ON(sv->menuItems[SV_MANIP_NONE].widget); // Next, the toggle that says whether we replace current // manipulators every time we change the type given in the menu. if (sv->curManipReplaces == TRUE ) TOGGLE_ON( sv->menuItems[SV_MANIP_REPLACE_ALL].widget ); else TOGGLE_OFF( sv->menuItems[SV_MANIP_REPLACE_ALL].widget ); break; case SV_LIGHT: // disable the add light entries if we have more than 8 lights if (sv->lightDataList.getLength() < 8) XtSetArg(args[0], XmNsensitive, True); else XtSetArg(args[0], XmNsensitive, False); XtSetValues(sv->menuItems[SV_LIGHT_ADD_DIRECT].widget, args, 1); XtSetValues(sv->menuItems[SV_LIGHT_ADD_POINT].widget, args, 1); XtSetValues(sv->menuItems[SV_LIGHT_ADD_SPOT].widget, args, 1); // update the headlight label (show on/off with '*') sv->isHeadlight() ? strcpy(str, "* ") : strcpy(str, " "); strcat(str, sv->headlightData->name); xmstr = XmStringCreate(str, XmSTRING_DEFAULT_CHARSET); XtSetArg(args[0], XmNlabelString, xmstr); XtSetValues(sv->headlightData->cascadeWidget, args, 1); XmStringFree(xmstr); // update the lights label (show on/off with '*') { for (int i=0; i < sv->lightDataList.getLength(); i++) { SvLightData *data = (SvLightData *) sv->lightDataList[i]; IS_LIGHT_ON(data->lightSwitch) ? strcpy(str, "* ") : strcpy(str, " "); strcat(str, data->name); xmstr = XmStringCreate(str, XmSTRING_DEFAULT_CHARSET); XtSetArg(args[0], XmNlabelString, xmstr); XtSetValues(data->cascadeWidget, args, 1); XmStringFree(xmstr); } } break; default: break; } } //////////////////////////////////////////////////////////////////////// // // Description: // Determines whether a given node is affected by a transform. // // Use: static, public // SbBool SoSceneViewer::isAffectedByTransform( SoNode *theNode ) // node to be affected? // //////////////////////////////////////////////////////////////////////// { if ( theNode->isOfType( SoGroup::getClassTypeId() ) || theNode->isOfType( SoShape::getClassTypeId() ) || theNode->isOfType( SoCamera::getClassTypeId() ) || theNode->isOfType( SoLight::getClassTypeId() ) ) { return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////// // // Description: // Determines whether a given node is affected by material node. // // Use: static, public // SbBool SoSceneViewer::isAffectedByMaterial( SoNode *theNode ) // node to be affected? // //////////////////////////////////////////////////////////////////////// { if ( theNode->isOfType( SoGroup::getClassTypeId() ) || theNode->isOfType( SoShape::getClassTypeId() ) ) { return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////// // // Description: // Create the lights and camera environment structure. // // Use: private // void SoSceneViewer::createLightsCameraEnvironment() // //////////////////////////////////////////////////////////////////////// { // Group { // Label { "SoSceneViewer Environment v3.0" } // Camera {} // Environment {} // Group { // Switch { Light 1 } # switch is child 0, light is child 0 // Switch { Light 2 } # switch is child 1, light is child 0 // ... // } // } // // NOTE: since the camera may be switched by the viewer (ortho/perspective toggle) // make sure to get the camera from the viewer (and not cache the camera). lightsCameraEnvironment = new SoGroup; environment = new SoEnvironment; lightGroup = new SoGroup; envLabel = new SoLabel; envLabel->label.setValue(SV_ENV_LABEL); lightsCameraEnvironment->addChild(envLabel); #ifndef EXPLORER // Explorer SceneViewer doesn't remove cameras from the Scene, so // don't add any... lightsCameraEnvironment->addChild(new SoPerspectiveCamera); #endif lightsCameraEnvironment->addChild(environment); lightsCameraEnvironment->addChild(lightGroup); } //////////////////////////////////////////////////////////////////////// // // Description: // Brings up the "About..." dialog (same code as gavin demo programs) // // Use: private // void SoSceneViewer::showAboutDialog() // //////////////////////////////////////////////////////////////////////// { if (access("/usr/demos/Inventor/SceneViewer.about", R_OK) != 0) { system("xmessage 'Sorry, could not find " "/usr/demos/Inventor/SceneViewer.about' > /dev/null"); return; } char command[100]; sprintf(command, "which acroread > /dev/null"); int err = system(command); if (err) { system("xmessage 'You must install acroread" " for this function to work' > /dev/null"); return; } sprintf(command, "acroread /usr/demos/Inventor/SceneViewer.about &"); system(command); } // // define those generic virtual functions // const char * SoSceneViewer::getDefaultWidgetName() const { return "SoSceneViewer"; } const char * SoSceneViewer::getDefaultTitle() const { return "SceneViewer"; } const char * SoSceneViewer::getDefaultIconTitle() const { return "SceneViewer"; }