/*
*
* 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/
*
*/
/*-------------------------------------------------------------
* This is an example from the Inventor Mentor,
* volume 1, chapter 15, example 3.
*
* Manipulator attachment example.
*
* The scene graph has an SoWrapperKit, a cube and a sphere.
* A file containing a luxo lamp is read in as the 'contents'
* part of the SoWrapperKit.
* When the user picks on the SoWrapperKit (lamp), the kit's
* "transform" part is replaced with an SoTransformBoxManip.
* Alternatively, when the user picks the sphere, the
* sphere's associated transform is replaced by an
* SoHandleBoxManip. Picking the cube causes an
* SoTrackballManip to replace the cube's transform.
*
* Manipulator callbacks are used to change
* the color of the object being manipulated.
*
* Note that for illustration purposes, the
* cube and SoWrapperKit already have transform nodes
* associated with them; the sphere does not. In all cases,
* the routine createTransformPath() is used to find the
* transform node that affects the picked object.
*-----------------------------------------------------------*/
#include <stdlib.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/manips/SoHandleBoxManip.h>
#include <Inventor/manips/SoTrackballManip.h>
#include <Inventor/manips/SoTransformBoxManip.h>
#include <Inventor/nodekits/SoWrapperKit.h>
#include <Inventor/nodes/SoCamera.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoLight.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSelection.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTransform.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
// function prototypes
void selectionCallback(void *, SoPath *);
void deselectionCallback(void *, SoPath *);
void dragStartCallback(void *, SoDragger *);
void dragFinishCallback(void *, SoDragger *);
// global data
SoSeparator *root;
SoHandleBoxManip *myHandleBox;
SoTrackballManip *myTrackball;
SoTransformBoxManip *myTransformBox;
SoPath *handleBoxPath = NULL;
SoPath *trackballPath = NULL;
SoPath *transformBoxPath = NULL;
int
main(int, char **argv)
{
// Initialize Inventor and Xt
Widget myWindow = SoXt::init(argv[0]);
if (myWindow == NULL) exit(1);
// create and set up the selection node
SoSelection *selectionRoot = new SoSelection;
selectionRoot->ref();
selectionRoot->
addSelectionCallback(selectionCallback, NULL);
selectionRoot->
addDeselectionCallback(deselectionCallback, NULL);
// create the scene graph
root = new SoSeparator;
selectionRoot->addChild(root);
// Read a file into contents of SoWrapperKit
// Translate it to the right.
SoWrapperKit *myWrapperKit = new SoWrapperKit;
root->addChild(myWrapperKit);
SoInput myInput;
if (!myInput.openFile("/usr/share/src/Inventor/examples/data/luxo.iv"))
exit (1);
SoSeparator *objectFromFile = SoDB::readAll(&myInput);
if (objectFromFile == NULL)
exit (1);
myWrapperKit->setPart("contents",objectFromFile);
myWrapperKit->set("transform { translation 3 -1 0 }");
SoMaterial *wrapperMat
= (SoMaterial *) myWrapperKit->getPart("material",TRUE);
wrapperMat->diffuseColor.setValue(.8, .8, .8);
// Create a cube with its own transform.
SoSeparator *cubeRoot = new SoSeparator;
SoTransform *cubeXform = new SoTransform;
cubeXform->translation.setValue(-4, 0, 0);
root->addChild(cubeRoot);
cubeRoot->addChild(cubeXform);
SoMaterial *cubeMat = new SoMaterial;
cubeMat->diffuseColor.setValue(.8, .8, .8);
cubeRoot->addChild(cubeMat);
cubeRoot->addChild(new SoCube);
// add a sphere node without a transform
// (one will be added when we attach the manipulator)
SoSeparator *sphereRoot = new SoSeparator;
SoMaterial *sphereMat = new SoMaterial;
root->addChild(sphereRoot);
sphereRoot->addChild(sphereMat);
sphereRoot->addChild(new SoSphere);
sphereMat->diffuseColor.setValue(.8, .8, .8);
// create the manipulators
myHandleBox = new SoHandleBoxManip;
myHandleBox->ref();
myTrackball = new SoTrackballManip;
myTrackball->ref();
myTransformBox = new SoTransformBoxManip;
myTransformBox->ref();
// Get the draggers and add callbacks to them. Note
// that you don't put callbacks on manipulators. You put
// them on the draggers which handle events for them.
SoDragger *myDragger;
myDragger = myTrackball->getDragger();
myDragger->addStartCallback(dragStartCallback,cubeMat);
myDragger->addFinishCallback(dragFinishCallback,cubeMat);
myDragger = myHandleBox->getDragger();
myDragger->addStartCallback(dragStartCallback,sphereMat);
myDragger->addFinishCallback(dragFinishCallback,sphereMat);
myDragger = myTransformBox->getDragger();
myDragger->addStartCallback(dragStartCallback,wrapperMat);
myDragger->addFinishCallback(dragFinishCallback,wrapperMat);
SoXtExaminerViewer *myViewer
= new SoXtExaminerViewer(myWindow);
myViewer->setSceneGraph(selectionRoot);
myViewer->setTitle("Attaching Manipulators");
myViewer->show();
myViewer->viewAll();
SoXt::show(myWindow);
SoXt::mainLoop();
}
// Is this node of a type that is influenced by transforms?
SbBool
isTransformable(SoNode *myNode)
{
if (myNode->isOfType(SoGroup::getClassTypeId())
|| myNode->isOfType(SoShape::getClassTypeId())
|| myNode->isOfType(SoCamera::getClassTypeId())
|| myNode->isOfType(SoLight::getClassTypeId()))
return TRUE;
else
return FALSE;
}
// Create a path to the transform node that affects the tail
// of the input path. 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. Search siblings of path
// tail from right to left until you find a transform. If
// none is found, or if another transformable object is
// found (shape,group,light,or camera), then insert a
// transform just to the left of the tail. This way, the
// manipulator only effects the selected object.
// [3] The path-tail IS a group. Search its children left to
// right until a transform is found. If a transformable
// node is found first, insert a transform just left of
// that node. This way the manip will affect all nodes
// in the group.
SoPath *
createTransformPath(SoPath *inputPath)
{
int pathLength = inputPath->getLength();
if (pathLength < 2) // Won't be able to get parent of tail
return NULL;
SoNode *tail = inputPath->getTail();
// CASE 1: The tail is a node kit.
// Nodekits have built in policy for creating parts.
// The kit copies inputPath, then extends it past the
// kit all the way down to the transform. It creates the
// transform if necessary.
if (tail->isOfType(SoBaseKit::getClassTypeId())) {
SoBaseKit *kit = (SoBaseKit *) tail;
return kit->createPathToPart("transform",TRUE,inputPath);
}
SoTransform *editXf = NULL;
SoGroup *parent;
SbBool existedBefore = FALSE;
// CASE 2: The tail is not a group.
SbBool isTailGroup;
isTailGroup = tail->isOfType(SoGroup::getClassTypeId());
if (!isTailGroup) {
// 'parent' is node above tail. Search under parent right
// to left for a transform. If we find a 'movable' node
// insert a transform just left of tail.
parent = (SoGroup *) inputPath->getNode(pathLength - 2);
int tailIndx = parent->findChild(tail);
for (int i = tailIndx; (i >= 0) && (editXf == NULL);i--){
SoNode *myNode = parent->getChild(i);
if (myNode->isOfType(SoTransform::getClassTypeId()))
editXf = (SoTransform *) myNode;
else if (i != tailIndx && (isTransformable(myNode)))
break;
}
if (editXf == NULL) {
existedBefore = FALSE;
editXf = new SoTransform;
parent->insertChild(editXf, tailIndx);
}
else
existedBefore = TRUE;
}
// CASE 3: The tail is a group.
else {
// Search the children from left to right for transform
// nodes. Stop the search if we come to a movable node.
// and insert a transform before it.
parent = (SoGroup *) tail;
int i;
for (i = 0;
(i < parent->getNumChildren()) && (editXf == NULL);
i++) {
SoNode *myNode = parent->getChild(i);
if (myNode->isOfType(SoTransform::getClassTypeId()))
editXf = (SoTransform *) myNode;
else if (isTransformable(myNode))
break;
}
if (editXf == NULL) {
existedBefore = FALSE;
editXf = new SoTransform;
parent->insertChild(editXf, i);
}
else
existedBefore = TRUE;
}
// Create 'pathToXform.' Copy inputPath, then make last
// node be editXf.
SoPath *pathToXform = NULL;
pathToXform = inputPath->copy();
pathToXform->ref();
if (!isTailGroup) // pop off the last entry.
pathToXform->pop();
// add editXf to the end
int xfIndex = parent->findChild(editXf);
pathToXform->append(xfIndex);
pathToXform->unrefNoDelete();
return(pathToXform);
}
// This routine is called when an object
// gets selected. We determine which object
// was selected, then call replaceNode()
// to replace the object's transform with
// a manipulator.
void
selectionCallback(
void *, // user data is not used
SoPath *selectionPath)
{
// Attach the manipulator.
// Use the convenience routine to get a path to
// the transform that effects the selected object.
SoPath *xformPath = createTransformPath(selectionPath);
if (xformPath == NULL) return;
xformPath->ref();
// Attach the handle box to the sphere,
// the trackball to the cube
// or the transformBox to the wrapperKit
if (selectionPath->getTail()->isOfType(
SoSphere::getClassTypeId())) {
handleBoxPath = xformPath;
myHandleBox->replaceNode(xformPath);
}
else if (selectionPath->getTail()->
isOfType(SoCube::getClassTypeId())) {
trackballPath = xformPath;
myTrackball->replaceNode(xformPath);
}
else if (selectionPath->getTail()->
isOfType(SoWrapperKit::getClassTypeId())) {
transformBoxPath = xformPath;
myTransformBox->replaceNode(xformPath);
}
}
// This routine is called whenever an object gets
// deselected. It detaches the manipulator from
// the transform node, and removes it from the
// scene graph that will not be visible.
void
deselectionCallback(
void *, // user data is not used
SoPath *deselectionPath)
{
if (deselectionPath->getTail()->
isOfType(SoSphere::getClassTypeId())) {
myHandleBox->replaceManip(handleBoxPath,NULL);
handleBoxPath->unref();
}
else if (deselectionPath->getTail()->
isOfType(SoCube::getClassTypeId())) {
myTrackball->replaceManip(trackballPath,NULL);
trackballPath->unref();
}
else if (deselectionPath->getTail()->
isOfType(SoWrapperKit::getClassTypeId())) {
myTransformBox->replaceManip(transformBoxPath,NULL);
transformBoxPath->unref();
}
}
// This is called when a manipulator is
// about to begin manipulation.
void
dragStartCallback(
void *myMaterial, // user data
SoDragger *) // callback data not used
{
((SoMaterial *) myMaterial)->diffuseColor=SbColor(1,.2,.2);
}
// This is called when a manipulator is
// done manipulating.
void
dragFinishCallback(
void *myMaterial, // user data
SoDragger *) // callback data not used
{
((SoMaterial *) myMaterial)->diffuseColor=SbColor(.8,.8,.8);
}