Ejemplo n.º 1
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);
}
Ejemplo n.º 2
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);
	    }
	}
    }
}
Ejemplo n.º 3
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++;
      }
    }

  }
}