bool isVisibleFace(int faceIndex, const SbVec2f& pos, Gui::View3DInventorViewer* viewer)
    {
        SoSeparator* root = new SoSeparator;
        root->ref();
        root->addChild(viewer->getSoRenderManager()->getCamera());
        root->addChild(vp->getRoot());

        SoSearchAction searchAction;
        searchAction.setType(PartGui::SoBrepFaceSet::getClassTypeId());
        searchAction.setInterest(SoSearchAction::FIRST);
        searchAction.apply(root);
        SoPath* selectionPath = searchAction.getPath();

        SoRayPickAction rp(viewer->getSoRenderManager()->getViewportRegion());
        rp.setNormalizedPoint(pos);
        rp.apply(selectionPath);
        root->unref();

        SoPickedPoint* pick = rp.getPickedPoint();
        if (pick) {
            const SoDetail* detail = pick->getDetail();
            if (detail && detail->isOfType(SoFaceDetail::getClassTypeId())) {
                int index = static_cast<const SoFaceDetail*>(detail)->getPartIndex();
                if (faceIndex != index)
                    return false;
                SbVec3f dir = viewer->getViewDirection();
                const SbVec3f& nor = pick->getNormal();
                if (dir.dot(nor) > 0)
                    return false; // bottom side points to user
                return true;
            }
        }

        return false;
    }
Example #2
0
void
SoOrthoSlice::rayPick(SoRayPickAction * action)
{
  if (!this->shouldRayPick(action)) return;

  SoState * state = action->getState();
  if (!PRIVATE(this)->confirmValidInContext(state)) { return; }

  const CvrVoxelBlockElement * vbelem = CvrVoxelBlockElement::getInstance(state);
  if (vbelem == NULL) { return; }

  this->computeObjectSpaceRay(action);
  const SbLine & ray = action->getLine();

  const SbPlane sliceplane = PRIVATE(this)->getSliceAsPlane(action);

  SbVec3f intersection;
  if (sliceplane.intersect(ray, intersection) && // returns FALSE if parallel
      action->isBetweenPlanes(intersection)) {

    SbVec3s ijk = vbelem->objectCoordsToIJK(intersection);

    const SbVec3s & voxcubedims = vbelem->getVoxelCubeDimensions();
    const SbBox3s voxcubebounds(SbVec3s(0, 0, 0), voxcubedims - SbVec3s(1, 1, 1));

    if (voxcubebounds.intersect(ijk)) {

      SoPickedPoint * pp = action->addIntersection(intersection);
      // if NULL, something else is obstructing the view to the
      // volume, the app programmer only want the nearest, and we
      // don't need to continue our intersection tests
      if (pp == NULL) return;
      pp->setObjectNormal(sliceplane.getNormal());

      SoOrthoSliceDetail * detail = new SoOrthoSliceDetail;
      pp->setDetail(detail, this);

      detail->objectcoords = intersection;
      detail->ijkcoords = ijk;
      detail->voxelvalue = vbelem->getVoxelValue(ijk);

      if (CvrUtil::useFlippedYAxis()) {
        static SbBool flag = FALSE;
        if (!flag) {
          SoDebugError::postWarning("SoOrthoSlice::rayPick", 
                                    "RayPick'ing will not be correct for SoOrthoSlice when the "
                                    "obsolete CVR_USE_FLIPPED_Y_AXIS envvar is active.");
          flag = TRUE;
        }
      }

    }
  }

  // Common clipping plane handling.
  SoOrthoSlice::doAction(action);
}
void
myMouseCB(void *userData, SoEventCallback *eventCB)
{
   const SoEvent *event = eventCB->getEvent();
   SoSeparator * scenegraph = (SoSeparator*) userData;

   if (SO_MOUSE_PRESS_EVENT(event, BUTTON2)) 
   {
      std::cout << "mouse button 2" << std::endl;

      if (global_render_manager)
      {
         // attempting raypick in the event_cb() callback method
         SoRayPickAction rp( global_render_manager->getViewportRegion() );
         rp.setPoint(event->getPosition());
         rp.setPickAll(true);

         std::cout << "event position: " << event->getPosition()[0] << " " << event->getPosition()[1]
            << std::endl;
         
         rp.apply(global_render_manager->getSceneGraph());

         const SoPickedPointList& pickedPoints = rp.getPickedPointList();
         std::cout << "# of picked points: " << pickedPoints.getLength() << std::endl;
         for (int i=0; i<pickedPoints.getLength(); ++i)
         {
            SoPickedPoint * pickedPoint = pickedPoints[i];
            SbVec3f point = pickedPoint->getPoint();
            std::cout << "point " << i << " : " << point[0] << " " << point[1] << " " <<
               point[2] << std::endl;
         }
      }

      eventCB->setHandled();
   }
   else if (SO_MOUSE_PRESS_EVENT(event, BUTTON1)) 
   {
      //std::cout << "mouse button 1" << std::endl;
      eventCB->setHandled();
   }
   else if (SO_MOUSE_PRESS_EVENT(event, BUTTON3)) 
   {
      //std::cout << "mouse button 3" << std::endl;
      eventCB->setHandled();
   }
}
Example #4
0
void
_SoNurbsPickV4SurfaceMap::intersectTriangle()

//
////////////////////////////////////////////////////////////////////////
{
    SbVec3f             point;
    SbVec3f             barycentric;
    SbBool              onFrontSide;
    SoPickedPoint       *pp;

    if (!pickAction->intersect(SP[cacheIndices[0]].p,
                              SP[cacheIndices[1]].p,
                              SP[curPrimIndex].p,
                              point, barycentric, onFrontSide))
        return;

    pp = pickAction->addIntersection(point);
    if (pp != NULL) {

        SbVec3f         norm, n0, n1, n2;
        SbVec4f         texCoord;

        // Compute normal by interpolating vertex normals using
        // barycentric coordinates
	n0 = SP[0].norm;
	n1 = SP[1].norm;
	n2 = SP[2].norm;
	norm.setValue(barycentric, n0, n1, n2);
        norm.normalize();
        pp->setObjectNormal(norm);
        pp->setMaterialIndex(0);

        // Compute texture coordinates the same way
        texCoord[0] = barycentric[0] * TP[cacheIndices[0]][0] +
                      barycentric[1] * TP[cacheIndices[1]][0] +
                      barycentric[2] * TP[curPrimIndex][0];
        texCoord[1] = barycentric[0] * TP[cacheIndices[0]][1] +
                      barycentric[1] * TP[cacheIndices[1]][1] +
                      barycentric[2] * TP[curPrimIndex][1];
        texCoord[2] = 0.0;
        texCoord[3] = 1.0;
        pp->setObjectTextureCoords(texCoord);
    }
}
Example #5
0
void SoRing::rayPick(SoRayPickAction * action)
{
  // Is it pickable ?
  if ( ! shouldRayPick(action) ) return;

  // compute the picking ray in our current object space
  computeObjectSpaceRay(action);

  SoPickedPoint* pp;
  SbVec3f intersection;
  SbPlane XY(SbVec3f(0,0,1),0);
  if ( XY.intersect(action->getLine(), intersection) )
  {
    float x, y, z;
    intersection.getValue(x, y, z);

    // back to the case of a disk centered at (0,0)
    float Xc, Yc;
    center.getValue().getValue(Xc,Yc);
    x -= Xc;
    y -= Yc;

    // within radius ?
    if ( sqrt(x*x+y*y+z*z) > outerRadius.getValue() ) return;
    if ( sqrt(x*x+y*y+z*z) < innerRadius.getValue() ) return;

    // within angular section ?
    float theta = sweepAngle.getValue();
    float angle = atan2(y,x);
    if ( angle < 0. ) angle += 2*M_PI;
    if ( theta != 360 && 
         angle*180.F/M_PI > theta ) return;

    if ( action->isBetweenPlanes(intersection) &&
         (pp = action->addIntersection(intersection)) != NULL )
    {
      pp->setObjectNormal(normal);
      pp->setObjectTextureCoords(SbVec4f(0.5f*(1+cos(angle)),
                                         0.5f*(1+sin(angle)), 0, 1));
    }
  }

}
SbBool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::seekToPoint(const SbVec2s screenpos)
{

    SoRayPickAction rpaction(getSoRenderManager()->getViewportRegion());
    rpaction.setPoint(screenpos);
    rpaction.setRadius(2);
    rpaction.apply(getSoRenderManager()->getSceneGraph());

    SoPickedPoint* picked = rpaction.getPickedPoint();

    if(!picked) {
        this->interactiveCountInc(); // decremented in setSeekMode(FALSE)
        this->setSeekMode(FALSE);
        return FALSE;
    }

    SbVec3f hitpoint;
    hitpoint = picked->getPoint();

    this->seekToPoint(hitpoint);
    return TRUE;
}
Example #7
0
void
_SoNurbsPickV4CurveMap::intersectLine()

//
////////////////////////////////////////////////////////////////////////
{
    SbVec3f             point;
    SoPickedPoint       *pp;

    if (!pickAction->intersect(CP[0], CP[1], point))
        return;

    pp = pickAction->addIntersection(point);
    if (pp != NULL) {

        SbVec3f         norm;
        SbVec4f         texCoord;
        float           ratioFromV1;

        // Compute normal as vector pointing back along the pick ray.
	norm = -pickAction->getLine().getDirection();
        norm.normalize();
        pp->setObjectNormal(norm);
        pp->setMaterialIndex(0);

        // Compute interpolated texture coordinate 
        ratioFromV1 = ((point - CP[0]).length() /
                       (CP[1] - CP[0]).length());
        texCoord[0] = (TP[0][0] * (1.0 - ratioFromV1) +
                    TP[1][0] * ratioFromV1);
        texCoord[1] = (TP[0][1] * (1.0 - ratioFromV1) +
                    TP[1][1] * ratioFromV1);
        texCoord[2] = 0.0;
        texCoord[3] = 1.0;
        pp->setObjectTextureCoords(texCoord);
    }
}
Example #8
0
void
SoCylinder::rayPick(SoRayPickAction *action)
//
////////////////////////////////////////////////////////////////////////
{
    // First see if the object is pickable
    if (! shouldRayPick(action))
	return;

    int			curParts =(parts.isIgnored() ? ALL : parts.getValue());
    SbLine		pickLine;
    float		radius, halfHeight;
    SbVec3f		enterPoint, exitPoint, normal;
    SbVec4f		texCoord;
    SoPickedPoint	*pp;
    SoCylinderDetail	*detail;
    SbBool		materialPerPart;
    int			numHits = 0;

    // Compute the picking ray in our current object space
    computeObjectSpaceRay(action);

    // Get size of this cylinder
    getSize(radius, halfHeight);

    // Construct an infinite cylinder to test sides for intersection
    SbCylinder	infiniteCyl;
    infiniteCyl.setRadius(radius);

    SoMaterialBindingElement::Binding mbe =
	SoMaterialBindingElement::get(action->getState());
    materialPerPart =
	(mbe == SoMaterialBindingElement::PER_PART_INDEXED ||
	 mbe == SoMaterialBindingElement::PER_PART);

    // See if the line intersects the cylinder
    if (HAS_PART(curParts, SIDES) &&
	infiniteCyl.intersect(action->getLine(), enterPoint, exitPoint)) {

	// See if the enter point is within the real cylinder and is
	// between the near and far clipping planes.
	if (enterPoint[1] <= halfHeight && enterPoint[1] >= -halfHeight) {

	    numHits++;

	    if (action->isBetweenPlanes(enterPoint) &&
		(pp = action->addIntersection(enterPoint)) != NULL) {

		// The normal at the point is the same as the point but
		// with a 0 y coordinate
		normal.setValue(enterPoint[0], 0.0, enterPoint[2]);
		normal.normalize();
		pp->setObjectNormal(normal);

		texCoord.setValue(atan2f(enterPoint[0], enterPoint[2])
				  * (1.0 / (2.0 * M_PI)) + 0.5,
				  (enterPoint[1] + halfHeight) /
				  (2.0 * halfHeight),
				  0.0, 1.0);
		pp->setObjectTextureCoords(texCoord);

		detail = new SoCylinderDetail();
		detail->setPart(SIDES);
		pp->setDetail(detail, this);
	    }
	}

	// Do same for exit point
	if (exitPoint[1] <= halfHeight && exitPoint[1] >= -halfHeight) {
	    numHits++;

	    if (action->isBetweenPlanes(exitPoint) &&
		(pp = action->addIntersection(exitPoint)) != NULL) {
		normal.setValue(exitPoint[0], 0.0, exitPoint[2]);
		normal.normalize();
		pp->setObjectNormal(normal);
		texCoord.setValue(atan2f(exitPoint[0], exitPoint[2])
				  * (1.0 / (2.0 * M_PI)) + 0.5,
				  (exitPoint[1] + halfHeight) /
				  (2.0 * halfHeight),
				  0.0, 1.0);
		pp->setObjectTextureCoords(texCoord);
		detail = new SoCylinderDetail();
		detail->setPart(SIDES);
		pp->setDetail(detail, this);
	    }
	}
    }

    // If we haven't hit the cylinder twice already, check for an
    // intersection with the top face
    if (numHits < 2 && HAS_PART(curParts, TOP)) {
	SbVec3f	norm(0.0, 1.0, 0.0);

	// Construct a plane containing the top face
	SbPlane	topFacePlane(norm, halfHeight);

	// See if the ray hits this plane
	if (topFacePlane.intersect(action->getLine(), enterPoint)) {

	    // See if the intersection is within the correct radius
	    // and is within the clipping planes
	    float distFromYAxisSquared = (enterPoint[0] * enterPoint[0] +
					  enterPoint[2] * enterPoint[2]);

	    if (distFromYAxisSquared <= radius * radius) {

		numHits++;

		if (action->isBetweenPlanes(enterPoint) &&
		    (pp = action->addIntersection(enterPoint)) != NULL) {
		    pp->setObjectNormal(norm);
		    texCoord.setValue(0.5 + enterPoint[0] / (2.0 * radius),
				      0.5 - enterPoint[2] / (2.0 * radius),
				      0.0, 1.0);
		    pp->setObjectTextureCoords(texCoord);
		    if (materialPerPart)
			pp->setMaterialIndex(1);
		    detail = new SoCylinderDetail();
		    detail->setPart(TOP);
		    pp->setDetail(detail, this);
		}
	    }
	}
    }

    // If we haven't hit the cylinder twice already, check for an
    // intersection with the bottom face
    if (numHits < 2 && HAS_PART(curParts, BOTTOM)) {
	SbVec3f	norm(0.0, -1.0, 0.0);

	// Construct a plane containing the bottom face
	SbPlane		bottomFacePlane(norm, halfHeight);

	// See if the ray hits this plane
	if (bottomFacePlane.intersect(action->getLine(), enterPoint)) {

	    // See if the intersection is within the correct radius
	    // and is within the clipping planes
	    float distFromYAxisSquared = (enterPoint[0] * enterPoint[0] +
					  enterPoint[2] * enterPoint[2]);

	    if (distFromYAxisSquared <= radius * radius &&
		action->isBetweenPlanes(enterPoint) &&
		(pp = action->addIntersection(enterPoint)) != NULL) {
		pp->setObjectNormal(norm);
		texCoord.setValue(0.5 + enterPoint[0] / (2.0 * radius),
				  0.5 + enterPoint[2] / (2.0 * radius),
				  0.0, 1.0);
		pp->setObjectTextureCoords(texCoord);
		if (materialPerPart)
		    pp->setMaterialIndex(2);
		detail = new SoCylinderDetail();
		detail->setPart(BOTTOM);
		pp->setDetail(detail, this);
	    }
	}
    }
}
Example #9
0
void
SmTextureText2::rayPick(SoRayPickAction * action)
{
  //FIXME: Support multiple strings at one position (multiline text)?
  //For now, assumes 1 string at each position

  SoState * state = action->getState();

  const SbString * strings;
  const int numstrings = this->getStrings(state, strings);

  const int32_t * indices;
  const int numindices = this->getStringIndices(state, indices);

  const int num = numindices > 0 ? numindices : numstrings;

  this->computeObjectSpaceRay(action);
  const SmTextureFont::FontImage * font = SmTextureFontElement::get(state);
  SoMaterialBindingElement::Binding binding = SoMaterialBindingElement::get(state);

  for (int i = 0; i < num; i++){
    int idx = numindices > 0 ? indices[i] : i;
    SbVec3f p0, p1, p2, p3;
    this->buildStringQuad(action, idx, p0, p1, p2, p3);

    SbVec3f isect;
    SbVec3f bary;
    SbBool front;
    SbBool hit = action->intersect(p0, p1, p2, isect, bary, front);
    if (!hit) hit = action->intersect(p0, p2, p3, isect, bary, front);
    if (hit && action->isBetweenPlanes(isect)) {
      if (!this->pickOnPixel.getValue()){
        SoPickedPoint * pp = action->addIntersection(isect);
        if (pp) {
          SoTextDetail * detail = new SoTextDetail;
          detail->setStringIndex(idx);
          detail->setCharacterIndex(0);
          pp->setDetail(detail, this);
          pp->setMaterialIndex(binding == SoMaterialBindingElement::OVERALL ? 0 : idx);
          pp->setObjectNormal(SbVec3f(0.0f, 0.0f, 1.0f));
        }
        return;//We only calculate 1 picked point per SmTextureText2 instance
      }
      //else:
      //FIXME: Pixel coordinates are off by at least one pixel. 
      //Probably a rounding issue, or related to using center vs lowerleft coordinates of each pixel
      //Find out if the fault lies here, in buildStringQuad or in renderString... wiesener 20091102

      // find normalized 2D hitpoint on quad
      float h = (p3-p0).length();
      float w = (p1-p0).length();

      SbLine horizontal(p2, p3);
      SbVec3f ptonline = horizontal.getClosestPoint(isect);
      float vdist = (ptonline-isect).length();
      vdist /= h;

      SbLine vertical(p0, p3);
      ptonline = vertical.getClosestPoint(isect);
      float hdist = (ptonline-isect).length();
      hdist /= w;

      const SbString & string = strings[idx];
      int width_pixels = font->stringWidth(string);
      int height_pixels = font->height();

      int px = static_cast<int>(width_pixels * hdist);
      int py = static_cast<int>(height_pixels * vdist);

      const char * cstr = string.getString();
      int charindex = 0;

      int lastwidth = 0;
      while (charindex < string.getLength()){
        unsigned char c = cstr[charindex];
        int width = font->getKerning(c, 0);//should return "true width" of the glyph
        int xoffset = font->getXOffset(c);//will be negative for fonts extending to the left
        if (lastwidth + width > px){//we have a character, now let's see if it is a pixel hit
          SbVec2s glyphpos = font->getGlyphPositionPixels(c);//upper left pixel
          int x = px - lastwidth - xoffset + glyphpos[0];
          int y = py + glyphpos[1];
          const SbImage * img = font->getGLImage()->getImage();
          SbVec2s size;
          int nc;
          const unsigned char * pixels = img->getValue(size, nc);

//#define DEBUG_CHARACTER
#ifdef DEBUG_CHARACTER
          for (int fooy = 0; fooy < font->height(); fooy++){
            for (int foox = 0; foox < width - xoffset; foox++){
              int line = glyphpos[1] + fooy;
              char transparency = pixels[(line * size[0] + foox + glyphpos[0]) * nc];
              if (fooy == py && foox == px - lastwidth - xoffset) printf("X");
              else printf(transparency != 0 ? "O" : " ");
            }
            printf("\n");
          }
          printf("--------\n");
#endif //DEBUG_CHARACTER

          const unsigned char * pixel = &pixels[(y * size[0] + x) * nc];
          if (pixel[0] != 0){//not completely transparent
            SoPickedPoint * pp = action->addIntersection(isect);
            if (pp) {
              SoTextDetail * detail = new SoTextDetail;
              detail->setStringIndex(idx);
              detail->setCharacterIndex(0);
              pp->setDetail(detail, this);
              pp->setMaterialIndex(binding == SoMaterialBindingElement::OVERALL ? 0 : idx);
              pp->setObjectNormal(SbVec3f(0.0f, 0.0f, 1.0f));
            }
            return;//We only calculate 1 picked point per SmTextureText2 instance
          }
        }
        unsigned char c2 = cstr[charindex + 1];
        lastwidth += font->getKerning(c, c2);//add the kerning instead of width here
        if (lastwidth + font->getXOffset(c2) > px) break;//passed the point clicked
        charindex++;
      }
    }

  }
}
Example #10
0
/*!
  The string returned from this function is only valid until the next variable is
  requested.
*/
const char *
SoScXMLStateMachine::getVariable(const char * key) const
{
  if (strncmp(key, "_event.", 7) == 0) {
    // printf("scan for key '%s'\n", key);
    const char * subkey = key + 7;
    const ScXMLEvent * ev = this->getCurrentEvent();
    if (ev->isOfType(SoScXMLEvent::getClassTypeId())) {
      const SoScXMLEvent * soev = static_cast<const SoScXMLEvent *>(ev);
      const SoEvent * coinev = soev->getSoEvent();

      if (strcmp(subkey, "getTime()") == 0) {
        SbTime timeval = coinev->getTime();
        double doubletime = timeval.getValue();
        PRIVATE(this)->varstring = SbStringConvert::toString(doubletime);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getPosition().x") == 0) {
        SbVec2s pos = coinev->getPosition();
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[0]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getPosition().y") == 0) {
        SbVec2s pos = coinev->getPosition();
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[1]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getPosition()") == 0) {
        SbVec2s pos = coinev->getPosition();
        PRIVATE(this)->varstring = SbStringConvert::toString(pos);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getNormalizedPosition().x") == 0) {
        SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[0]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getNormalizedPosition().y") == 0) {
        SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
        PRIVATE(this)->varstring = SbStringConvert::toString(static_cast<double>(pos[1]));
        return PRIVATE(this)->varstring.getString();
      }
      else if (strcmp(subkey, "getNormalizedPosition()") == 0) {
        SbVec2f pos = coinev->getNormalizedPosition(this->getViewportRegion());
        PRIVATE(this)->varstring = SbStringConvert::toString(pos);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "wasShiftDown()") == 0) {
        SbBool wasdown = coinev->wasShiftDown();
        PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "wasCtrlDown()") == 0) {
        SbBool wasdown = coinev->wasCtrlDown();
        PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "wasAltDown()") == 0) {
        SbBool wasdown = coinev->wasAltDown();
        PRIVATE(this)->varstring = SbStringConvert::toString<bool>(wasdown);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getState()") == 0 && coinev->isOfType(SoButtonEvent::getClassTypeId())) {
        const SoButtonEvent * bevent = coin_assert_cast<const SoButtonEvent *>(coinev);
        SbString enumname;
        SoButtonEvent::enumToString(bevent->getState(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getKey()") == 0 && coinev->isOfType(SoKeyboardEvent::getClassTypeId())) {
        const SoKeyboardEvent * kbevent = coin_assert_cast<const SoKeyboardEvent *>(coinev);
        SbString enumname;
        SoKeyboardEvent::enumToString(kbevent->getKey(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getPrintableCharacter()") == 0 && coinev->isOfType(SoKeyboardEvent::getClassTypeId())) {
        const SoKeyboardEvent * kbevent = coin_assert_cast<const SoKeyboardEvent *>(coinev);
        char printable = kbevent->getPrintableCharacter();
        PRIVATE(this)->varstring.sprintf("'%c'", printable);
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getButton()") == 0 && coinev->isOfType(SoMouseButtonEvent::getClassTypeId())) {
        const SoMouseButtonEvent * mbevent = coin_assert_cast<const SoMouseButtonEvent *>(coinev);
        SbString enumname;
        SoMouseButtonEvent::enumToString(mbevent->getButton(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      else if (strcmp(subkey, "getButton()") == 0 && coinev->isOfType(SoSpaceballButtonEvent::getClassTypeId())) {
        const SoSpaceballButtonEvent * mbevent = coin_assert_cast<const SoSpaceballButtonEvent *>(coinev);
        SbString enumname;
        SoSpaceballButtonEvent::enumToString(mbevent->getButton(), enumname);
        PRIVATE(this)->varstring.sprintf("'%s'", enumname.getString());
        return PRIVATE(this)->varstring.getString();
      }

      // FIXME: x., .y, .z
      else if (strcmp(subkey, "getTranslation()") == 0 && coinev->isOfType(SoMotion3Event::getClassTypeId())) {
        const SoMotion3Event * m3event = coin_assert_cast<const SoMotion3Event *>(coinev);
        SbVec3f translation = m3event->getTranslation();
        PRIVATE(this)->varstring = SbStringConvert::toString(translation);
        return PRIVATE(this)->varstring.getString();
      }

      // FIXME: .angle, .axis
      else if (strcmp(subkey, "getRotation()") == 0 && coinev->isOfType(SoMotion3Event::getClassTypeId())) {
        const SoMotion3Event * m3event = coin_assert_cast<const SoMotion3Event *>(coinev);
        SbRotation rotation = m3event->getRotation();
        PRIVATE(this)->varstring = SbStringConvert::toString(rotation);
        return PRIVATE(this)->varstring.getString();
      }

      // FIXME: make this into a evaluator-level RayPick(SbVec2f) function instead
      else if (strcmp(key + 7, "pickposition3") == 0) {
        SbVec2s location2 = coinev->getPosition();
        SoRayPickAction rpa(this->getViewportRegion());
        rpa.setPoint(location2);
        rpa.apply(this->getSceneGraphRoot());
        SoPickedPoint * pp = rpa.getPickedPoint();
        if (pp) {
          SbVec3f pickpos = pp->getPoint();
          PRIVATE(this)->varstring = SbStringConvert::toString(pickpos);
        } else {
          PRIVATE(this)->varstring.sprintf("FALSE"); // need a valid undefined-value
        }
        return PRIVATE(this)->varstring.getString();
      }
    }
  }

  else if (strncmp(key, "coin:", 5) == 0) {
    const char * subkey = key + 5;
    if (strncmp(subkey, "camera.", 7) == 0) {
      SoCamera * camera = this->getActiveCamera();
      if (!camera) {
        SoDebugError::post("SoScXMLStateMachine::getVariable",
                           "queried for camera, but no camera is set.");
        return NULL;
      }
      const char * detail = subkey + 7;
      if (strcmp(detail, "getTypeId()") == 0) {
        PRIVATE(this)->varstring.sprintf("'%s'", camera->getTypeId().getName().getString());
        return PRIVATE(this)->varstring.getString();
      }
    }

    // get generic field access working and intercept for more So-specific stuff
    // coin:viewport
    // coin:camera
    // coin:scene
  }

  //else {
  //}

  // couldn't resolve the symbol - try parent class to get '_data' and other '_event'
  // locations resolved
  return inherited::getVariable(key);
}