void SoCylinder::computeBBox(SoAction *, SbBox3f &box, SbVec3f ¢er) // //////////////////////////////////////////////////////////////////////// { int curParts = (parts.isIgnored() ? ALL : parts.getValue()); if (curParts == 0) // No parts at all! box.setBounds(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); else { float r, h; SbVec3f min, max; getSize(r, h); if (HAS_PART(curParts, SIDES | TOP)) max.setValue( r, h, r); else max.setValue( r, -h, r); if (HAS_PART(curParts, SIDES | BOTTOM)) min.setValue(-r, -h, -r); else min.setValue(-r, h, -r); box.setBounds(min, max); } center.setValue(0.0, 0.0, 0.0); }
/*! Turns on a part of the arrow. */ void SoArrow::addPart(Part part) { arrowHeads.setValue(arrowHeads.getValue() | part); if (HAS_PART(arrowHeads.getValue(), SoArrow::BEGIN)) { calEngine->c.setValue(1); beginSw->whichChild.setValue(SO_SWITCH_ALL); } else { calEngine->c.setValue(0); beginSw->whichChild.setValue(SO_SWITCH_NONE); } if (HAS_PART(arrowHeads.getValue(), SoArrow::END)) { calEngine->d.setValue(1); endSw->whichChild.setValue(SO_SWITCH_ALL); } else { calEngine->d.setValue(0); endSw->whichChild.setValue(SO_SWITCH_NONE); } }
void SoCylinder::GLRenderGeneric(SoGLRenderAction *action, SbBool sendNormals, SbBool doTextures) // //////////////////////////////////////////////////////////////////////// { SbVec3f scale, tmp; getSize(scale[0], scale[1]); scale[2] = scale[0]; SbBool materialPerPart; int curParts, numSides, numSections, side, section; float yTop, yBot, dy; float s, ds, tTop, tBot, dt; float outerRadius, innerRadius, dRadius; SbVec2f *ringCoords; SbVec3f pt, norm; SoMaterialBundle mb(action); SoMaterialBindingElement::Binding mbe = SoMaterialBindingElement::get(action->getState()); materialPerPart = (mbe == SoMaterialBindingElement::PER_PART_INDEXED || mbe == SoMaterialBindingElement::PER_PART); curParts = (parts.isIgnored() ? ALL : parts.getValue()); // Compute number of sides and sections to use to represent // cylinder, then compute ring of x,z coordinates around cylinder // and store in ringCoords. computeRing(action, numSides, numSections, ringCoords); // Make sure first material is sent if necessary mb.sendFirst(); if (HAS_PART(curParts, SIDES)) { // Draw each section of sides as a triangle mesh, from top to bottom yTop = 1.0; dy = -2.0 / numSections; tTop = 1.0; dt = -1.0 / numSections; ds = -1.0 / numSides; for (section = 0; section < numSections; section++) { yBot = yTop + dy; tBot = tTop + dt; s = 1.0; glBegin(GL_TRIANGLE_STRIP); for (side = 0; side < numSides; side++) { pt[0] = ringCoords[side][0]; pt[2] = ringCoords[side][1]; // Deal with normal norm.setValue(pt[0], 0.0, pt[2]); if (sendNormals) glNormal3fv(norm.getValue()); // Point at bottom of section pt[1] = yBot; if (doTextures) glTexCoord2f(s, tBot); glVertex3fv(SCALE(pt).getValue()); // Point at top of section pt[1] = yTop; if (doTextures) glTexCoord2f(s, tTop); glVertex3fv(SCALE(pt).getValue()); s += ds; } // Join end of strip back to beginning side = 0; s = 0.0; pt[0] = ringCoords[side][0]; pt[2] = ringCoords[side][1]; // Deal with normal norm.setValue(pt[0], 0.0, pt[2]); if (sendNormals) glNormal3fv(norm.getValue()); // Point at bottom of section pt[1] = yBot; if (doTextures) glTexCoord2f(s, tBot); glVertex3fv(SCALE(pt).getValue()); // Point at top of section pt[1] = yTop; if (doTextures) glTexCoord2f(s, tTop); glVertex3fv(SCALE(pt).getValue()); s += ds; glEnd(); // Prepare for next section down yTop = yBot; tTop = tBot; } } // Draw top face as a series of concentric rings. The number of // rings is the same as the number of sections of the sides of the // cylinder. if (HAS_PART(curParts, TOP)) { norm.setValue(0.0, 1.0, 0.0); pt[1] = 1.0; if (materialPerPart) mb.send(1, FALSE); if (sendNormals) glNormal3fv(norm.getValue()); // Start at the outside and work in outerRadius = 1.0; dRadius = -1.0 / numSections; for (section = numSections - 1; section >= 0; --section) { innerRadius = outerRadius + dRadius; // Innermost ring is treated as a triangle fan. This not // only gets better shading (because the center vertex is // sent), but also avoids the problem of having a polygon // with too many vertices. if (section == 0) { glBegin(GL_TRIANGLE_FAN); // Center point comes first pt[0] = pt[2] = 0.0; if (doTextures) glTexCoord2f(0.5, 0.5); glVertex3fv(SCALE(pt).getValue()); // Send all vertices around ring. Go in reverse order // so that vertex ordering is correct for (side = numSides - 1; side >= 0; side--) { pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(TOP_TEX_S(pt[0]), TOP_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); } // Send first vertex again pt[0] = outerRadius * ringCoords[numSides - 1][0]; pt[2] = outerRadius * ringCoords[numSides - 1][1]; if (doTextures) glTexCoord2f(TOP_TEX_S(pt[0]), TOP_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); glEnd(); } // Other rings are triangle strips else { glBegin(GL_TRIANGLE_STRIP); for (side = 0; side < numSides; side++) { // Send points on outer and inner rings pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(TOP_TEX_S(pt[0]), TOP_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); pt[0] = innerRadius * ringCoords[side][0]; pt[2] = innerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(TOP_TEX_S(pt[0]), TOP_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); } // Join end of strip back to beginning pt[0] = outerRadius * ringCoords[0][0]; pt[2] = outerRadius * ringCoords[0][1]; if (doTextures) glTexCoord2f(TOP_TEX_S(pt[0]), TOP_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); pt[0] = innerRadius * ringCoords[0][0]; pt[2] = innerRadius * ringCoords[0][1]; if (doTextures) glTexCoord2f(TOP_TEX_S(pt[0]), TOP_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); glEnd(); // Prepare for next ring outerRadius = innerRadius; } } } // Draw bottom face the same way as the top if (HAS_PART(curParts, BOTTOM)) { norm.setValue(0.0, -1.0, 0.0); pt[1] = -1.0; if (materialPerPart) mb.send(2, FALSE); if (sendNormals) glNormal3fv(norm.getValue()); // Start at the outside and work in outerRadius = 1.0; dRadius = -1.0 / numSections; for (section = numSections - 1; section >= 0; --section) { innerRadius = outerRadius + dRadius; // Innermost ring is drawn as a triangle fan. This not // only gets better shading (because the center vertex is // sent), but also avoids the problem of having a polygon // with too many vertices. if (section == 0) { glBegin(GL_TRIANGLE_FAN); // Center point comes first pt[0] = pt[2] = 0.0; if (doTextures) glTexCoord2f(0.5, 0.5); glVertex3fv(SCALE(pt).getValue()); // Send all vertices around ring for (side = 0; side < numSides; side++) { pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(BOT_TEX_S(pt[0]), BOT_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); } // Send first vertex again pt[0] = outerRadius * ringCoords[0][0]; pt[2] = outerRadius * ringCoords[0][1]; if (doTextures) glTexCoord2f(BOT_TEX_S(pt[0]), BOT_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); glEnd(); } // Other rings are triangle strips else { glBegin(GL_TRIANGLE_STRIP); // Go in reverse order so that vertex ordering is correct for (side = numSides - 1; side >= 0; side--) { // Send points on outer and inner rings pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(BOT_TEX_S(pt[0]), BOT_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); pt[0] = innerRadius * ringCoords[side][0]; pt[2] = innerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(BOT_TEX_S(pt[0]), BOT_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); } // Join end of strip back to beginning side = numSides - 1; pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(BOT_TEX_S(pt[0]), BOT_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); pt[0] = innerRadius * ringCoords[side][0]; pt[2] = innerRadius * ringCoords[side][1]; if (doTextures) glTexCoord2f(BOT_TEX_S(pt[0]), BOT_TEX_T(pt[2])); glVertex3fv(SCALE(pt).getValue()); glEnd(); // Prepare for next ring outerRadius = innerRadius; } } } }
void SoCylinder::generatePrimitives(SoAction *action) // //////////////////////////////////////////////////////////////////////// { SbBool materialPerPart; int curParts, numSides, numSections, side, section; float yTop, yBot, dy; float s, ds, tTop, tBot, dt; float outerRadius, innerRadius, dRadius; SbVec2f *ringCoords; SbVec3f pt, norm; float radius, halfHeight; SbVec4f tex; SbBool genTexCoords; SoPrimitiveVertex pv; SoCylinderDetail detail; const SoTextureCoordinateElement *tce; SoMaterialBindingElement::Binding mbe = SoMaterialBindingElement::get(action->getState()); materialPerPart = (mbe == SoMaterialBindingElement::PER_PART_INDEXED || mbe == SoMaterialBindingElement::PER_PART); curParts = (parts.isIgnored() ? ALL : parts.getValue()); // Compute number of sides and sections to use to represent // cylinder, then compute ring of x,z coordinates around cylinder // and store in ringCoords. computeRing(action, numSides, numSections, ringCoords); pv.setDetail(&detail); // Determine whether we should generate our own texture coordinates switch (SoTextureCoordinateElement::getType(action->getState())) { case SoTextureCoordinateElement::EXPLICIT: genTexCoords = TRUE; break; case SoTextureCoordinateElement::FUNCTION: genTexCoords = FALSE; break; } // If we're not generating our own coordinates, we'll need the // texture coordinate element to get coords based on points/normals. if (! genTexCoords) tce = SoTextureCoordinateElement::getInstance(action->getState()); else { tex[2] = 0.0; tex[3] = 1.0; } getSize(radius, halfHeight); if (HAS_PART(curParts, SIDES)) { // Draw each section of sides as a triangle mesh, from top to bottom yTop = 1.0; dy = -2.0 / numSections; tTop = 1.0; dt = -1.0 / numSections; ds = -1.0 / numSides; for (section = 0; section < numSections; section++) { yBot = yTop + dy; tBot = tTop + dt; s = 1.0; detail.setPart(SIDES); beginShape(action, TRIANGLE_STRIP); for (side = 0; side < numSides; side++) { pt[0] = ringCoords[side][0]; pt[2] = ringCoords[side][1]; // Deal with normal norm.setValue(pt[0], 0.0, pt[2]); pv.setNormal(norm); // Point at bottom of section pt[1] = yBot; pt[0] *= radius; pt[1] *= halfHeight; pt[2] *= radius; if (genTexCoords) { tex[0] = s; tex[1] = tBot; } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); // Point at top of section pt[1] = yTop; pt[1] *= halfHeight; if (genTexCoords) { tex[0] = s; tex[1] = tTop; } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); s += ds; } // Join end of strip back to beginning side = 0; s = 0.0; pt[0] = ringCoords[side][0]; pt[2] = ringCoords[side][1]; // Deal with normal norm.setValue(pt[0], 0.0, pt[2]); pv.setNormal(norm); // Point at bottom of section pt[1] = yBot; pt[0] *= radius; pt[1] *= halfHeight; pt[2] *= radius; if (genTexCoords) { tex[0] = s; tex[1] = tBot; } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); // Point at top of section pt[1] = yTop; pt[1] *= halfHeight; if (genTexCoords) { tex[0] = s; tex[1] = tTop; } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); s += ds; endShape(); // Prepare for next section down yTop = yBot; tTop = tBot; } } // Draw top face as a series of concentric rings. The number of // rings is the same as the number of sections of the sides of the // cylinder. if (HAS_PART(curParts, TOP)) { norm.setValue(0.0, 1.0, 0.0); pt[1] = halfHeight; if (materialPerPart) pv.setMaterialIndex(1); pv.setNormal(norm); detail.setPart(TOP); // Start at the outside and work in outerRadius = 1.0; dRadius = -1.0 / numSections; for (section = numSections - 1; section >= 0; --section) { innerRadius = outerRadius + dRadius; // Innermost ring is treated as a triangle fan. This not // only gets better shading (because the center vertex is // sent), but also avoids the problem of having a polygon // with too many vertices. if (section == 0) { beginShape(action, TRIANGLE_FAN); // Center point comes first pt[0] = pt[2] = 0.0; if (genTexCoords) tex[0] = tex[1] = 0.5; else tex = tce->get(norm, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); // Send all vertices around ring. Go in reverse order // so that vertex ordering is correct for (side = numSides - 1; side >= 0; side--) { pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = TOP_TEX_S(pt[0]); tex[1] = TOP_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); } // Send first vertex again pt[0] = outerRadius * ringCoords[numSides - 1][0]; pt[2] = outerRadius * ringCoords[numSides - 1][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = TOP_TEX_S(pt[0]); tex[1] = TOP_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); endShape(); } // Other rings are triangle strips else { beginShape(action, TRIANGLE_STRIP); for (side = 0; side < numSides; side++) { // Send points on outer and inner rings pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = TOP_TEX_S(pt[0]); tex[1] = TOP_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); pt[0] = innerRadius * ringCoords[side][0]; pt[2] = innerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = TOP_TEX_S(pt[0]); tex[1] = TOP_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); } // Join end of strip back to beginning pt[0] = outerRadius * ringCoords[0][0]; pt[2] = outerRadius * ringCoords[0][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = TOP_TEX_S(pt[0]); tex[1] = TOP_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); pt[0] = innerRadius * ringCoords[0][0]; pt[2] = innerRadius * ringCoords[0][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = TOP_TEX_S(pt[0]); tex[1] = TOP_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); endShape(); // Prepare for next ring outerRadius = innerRadius; } } } // Draw bottom face the same way as the top if (HAS_PART(curParts, BOTTOM)) { norm.setValue(0.0, -1.0, 0.0); pt[1] = -halfHeight; if (materialPerPart) pv.setMaterialIndex(2); pv.setNormal(norm); detail.setPart(BOTTOM); // Start at the outside and work in outerRadius = 1.0; dRadius = -1.0 / numSections; for (section = numSections - 1; section >= 0; --section) { innerRadius = outerRadius + dRadius; // Innermost ring is drawn as a triangle fan. This not // only gets better shading (because the center vertex is // sent), but also avoids the problem of having a polygon // with too many vertices. if (section == 0) { beginShape(action, TRIANGLE_FAN); // Center point comes first pt[0] = pt[2] = 0.0; if (genTexCoords) tex[0] = tex[1] = 0.5; else tex = tce->get(norm, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); // Send all vertices around ring for (side = 0; side < numSides; side++) { pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = BOT_TEX_S(pt[0]); tex[1] = BOT_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); } // Send first vertex again pt[0] = outerRadius * ringCoords[0][0]; pt[2] = outerRadius * ringCoords[0][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = BOT_TEX_S(pt[0]); tex[1] = BOT_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); endShape(); } // Other rings are triangle strips else { beginShape(action, TRIANGLE_STRIP); // Go in reverse order so that vertex ordering is correct for (side = numSides - 1; side >= 0; side--) { // Send points on outer and inner rings pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = BOT_TEX_S(pt[0]); tex[1] = BOT_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); pt[0] = innerRadius * ringCoords[side][0]; pt[2] = innerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = BOT_TEX_S(pt[0]); tex[1] = BOT_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); } // Join end of strip back to beginning side = numSides - 1; pt[0] = outerRadius * ringCoords[side][0]; pt[2] = outerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = BOT_TEX_S(pt[0]); tex[1] = BOT_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); pt[0] = innerRadius * ringCoords[side][0]; pt[2] = innerRadius * ringCoords[side][1]; pt[0] *= radius; pt[2] *= radius; if (genTexCoords) { tex[0] = BOT_TEX_S(pt[0]); tex[1] = BOT_TEX_T(pt[2]); } else tex = tce->get(pt, norm); pv.setPoint(pt); pv.setTextureCoords(tex); shapeVertex(&pv); endShape(); // Prepare for next ring outerRadius = innerRadius; } } } }
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); } } } }
/*! This is called once to generate the child nodes that make up this complex shape. The child nodes consist of a cylinder, transforms, and cones. A calculator node caluculates the height of the cylinder from the total height field and the height of any present arrowheads. */ void SoArrow::generateChildren() { // This should be called once, that means children // doesn not have any children yet. assert(children->getLength() == 0); // Construct the begin arrowhead SoCone *cne = new SoCone; cne->height.connectFrom(&coneHeight); cne->bottomRadius.connectFrom(&coneRadius); SoTransform *beginTran = new SoTransform; beginTran->rotation.setValue(SbVec3f(1, 0, 0), (float)M_PI); SoSeparator *beginSep = new SoSeparator; beginSep->addChild(beginTran); beginSep->addChild(cne); beginSw = new SoSwitch; beginSw->addChild(beginSep); // Construct the end arrowhead SoTranslation *endTran = new SoTranslation; SoSeparator *endSep = new SoSeparator; endSep->addChild(endTran); endSep->addChild(cne); endSw = new SoSwitch; endSw->addChild(endSep); // Move the arrowheads to the ends of the shaft calEngine = new SoCalculator; calEngine->a.connectFrom(&height); calEngine->b.connectFrom(&coneHeight); if (HAS_PART(arrowHeads.getValue(), SoArrow::BEGIN)) { calEngine->c.setValue(1); beginSw->whichChild.setValue(SO_SWITCH_ALL); } else { calEngine->c.setValue(0); beginSw->whichChild.setValue(SO_SWITCH_NONE); } if (HAS_PART(arrowHeads.getValue(), SoArrow::END)) { calEngine->d.setValue(1); endSw->whichChild.setValue(SO_SWITCH_ALL); } else { calEngine->d.setValue(0); endSw->whichChild.setValue(SO_SWITCH_NONE); } // Calculate the height of the shaft calEngine->expression.set1Value(0, "oa = a - c*b - d*b"); // Compute the position where the begin cone needs to be moved to calEngine->expression.set1Value(1, "oA = vec3f(0.0, b/2.0, 0.0)"); // Compute the position where the end cone needs to be moved to calEngine->expression.set1Value(2, "oB = vec3f(0.0, a - b/2.0, 0.0)"); // Compute the position where the shaft needs to be moved to calEngine->expression.set1Value(3, "oC = vec3f(0.0, oa/2.0 + c*b, 0.0)"); beginTran->translation.connectFrom(&calEngine->oA); endTran->translation.connectFrom(&calEngine->oB); SoCylinder *shaft = new SoCylinder; shaft->radius.connectFrom(&cylRadius); shaft->height.connectFrom(&calEngine->oa); SoTranslation *shaftTran = new SoTranslation; shaftTran->translation.connectFrom(&calEngine->oC); SoSeparator *root = new SoSeparator; root->addChild(beginSw); root->addChild(endSw); root->addChild(shaftTran); root->addChild(shaft); children->append(root); }
/*! Returns whether a given part is on or off. */ SbBool SoArrow::hasPart(Part part) const { return HAS_PART(arrowHeads.getValue(), part); }