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)); } } }
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); } } } }