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