// 0___1___2___3 // | /| /| /| // | / | / | / | // |/ |/ |/ | // 4---5---6----7 // | /| /| /| // | / | / | / | // |/ |/ |/ | // 8---9--10--11 // | /| /| /| // | / | / | / | // |/ |/ |/ | // 12--13--14--15 void ViewerGL::Implementation::drawRenderingVAO(unsigned int mipMapLevel, int textureIndex, ViewerGL::DrawPolygonModeEnum polygonMode, bool background) { // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); assert( QGLContext::currentContext() == _this->context() ); bool useShader = _this->getBitDepth() != eImageBitDepthByte; ///the texture rectangle in image coordinates. The values in it are multiples of tile size. /// const TextureRect &roiRounded = this->displayTextures[textureIndex].texture->getTextureRect(); const TextureRect& roiNotRounded = this->displayTextures[textureIndex].roiNotRoundedToTileSize; ///This is the coordinates in the image being rendered where datas are valid, this is in pixel coordinates ///at the time we initialize it but we will convert it later to canonical coordinates. See 1) const double par = roiRounded.par; RectD canonicalRoIRoundedToTileSize; roiRounded.toCanonical_noClipping(mipMapLevel, par /*, rod*/, &canonicalRoIRoundedToTileSize); RectD canonicalRoINotRounded; roiNotRounded.toCanonical_noClipping(mipMapLevel, par, &canonicalRoINotRounded); ///the RoD of the image in canonical coords. RectD rod = _this->getRoD(textureIndex); bool clipToDisplayWindow; { QMutexLocker l(&this->clipToDisplayWindowMutex); clipToDisplayWindow = this->clipToDisplayWindow; } RectD rectClippedToRoI(canonicalRoIRoundedToTileSize); rectClippedToRoI.intersect(rod, &rectClippedToRoI); if (clipToDisplayWindow) { RectD canonicalProjectFormat; this->getProjectFormatCanonical(canonicalProjectFormat); rod.intersect(canonicalProjectFormat, &rod); rectClippedToRoI.intersect(canonicalProjectFormat, &rectClippedToRoI); } //if user RoI is enabled, clip the rod to that roi bool userRoiEnabled; { QMutexLocker l(&this->userRoIMutex); userRoiEnabled = this->userRoIEnabled; } ////The texture real size (r.w,r.h) might be slightly bigger than the actual ////pixel coordinates bounds r.x1,r.x2 r.y1 r.y2 because we clipped these bounds against the bounds ////in the ViewerInstance::renderViewer function. That means we need to draw actually only the part of ////the texture that contains the bounds. ////Notice that r.w and r.h are scaled to the closest Po2 of the current scaling factor, so we need to scale it up ////So it is in the same coordinates as the bounds. ///Edit: we no longer divide by the closestPo2 since the viewer now computes images at lower resolution by itself, the drawing ///doesn't need to be scaled. if (userRoiEnabled) { { QMutexLocker l(&this->userRoIMutex); //if the userRoI isn't intersecting the rod, just don't render anything if ( !rod.intersect(this->userRoI, &rod) ) { return; } } rectClippedToRoI.intersect(rod, &rectClippedToRoI); //clipTexCoords<RectD>(canonicalTexRect,rectClippedToRoI,texBottom,texTop,texLeft,texRight); } if (polygonMode != eDrawPolygonModeWhole) { /// draw only the plane defined by the wipe handle QPolygonF polygonPoints, polygonTexCoords; RectD floatRectClippedToRoI; floatRectClippedToRoI.x1 = rectClippedToRoI.x1; floatRectClippedToRoI.y1 = rectClippedToRoI.y1; floatRectClippedToRoI.x2 = rectClippedToRoI.x2; floatRectClippedToRoI.y2 = rectClippedToRoI.y2; Implementation::WipePolygonEnum polyType = this->getWipePolygon(floatRectClippedToRoI, polygonMode == eDrawPolygonModeWipeRight, &polygonPoints); if (polyType == Implementation::eWipePolygonEmpty) { ///don't draw anything return; } else if (polyType == Implementation::eWipePolygonPartial) { this->getPolygonTextureCoordinates(polygonPoints, canonicalRoIRoundedToTileSize, polygonTexCoords); this->bindTextureAndActivateShader(textureIndex, useShader); GL_GPU::glBegin(GL_POLYGON); for (int i = 0; i < polygonTexCoords.size(); ++i) { const QPointF & tCoord = polygonTexCoords[i]; const QPointF & vCoord = polygonPoints[i]; GL_GPU::glTexCoord2d( tCoord.x(), tCoord.y() ); GL_GPU::glVertex2d( vCoord.x(), vCoord.y() ); } GL_GPU::glEnd(); this->unbindTextureAndReleaseShader(useShader); } else { ///draw the all polygon as usual polygonMode = eDrawPolygonModeWhole; } } if (polygonMode == eDrawPolygonModeWhole) { const double pixelCenterOffset = 0.5; // draw vertices at the center of the first and last pixel in the texture, with the same texture coordinates rectClippedToRoI.x1 += pixelCenterOffset * par; rectClippedToRoI.x2 -= pixelCenterOffset * par; rectClippedToRoI.y1 += pixelCenterOffset; rectClippedToRoI.y2 -= pixelCenterOffset; ///Vertices are in canonical coords GLfloat vertices[32] = { (GLfloat)rod.left(), (GLfloat)rod.top(), //0 (GLfloat)rectClippedToRoI.x1 + pixelCenterOffset, (GLfloat)rod.top(), //1 (GLfloat)rectClippedToRoI.x2 - pixelCenterOffset, (GLfloat)rod.top(), //2 (GLfloat)rod.right(), (GLfloat)rod.top(), //3 (GLfloat)rod.left(), (GLfloat)rectClippedToRoI.y2 - pixelCenterOffset, //4 (GLfloat)rectClippedToRoI.x1, (GLfloat)rectClippedToRoI.y2, //5 (GLfloat)rectClippedToRoI.x2, (GLfloat)rectClippedToRoI.y2, //6 (GLfloat)rod.right(), (GLfloat)rectClippedToRoI.y2, //7 (GLfloat)rod.left(), (GLfloat)rectClippedToRoI.y1, //8 (GLfloat)rectClippedToRoI.x1, (GLfloat)rectClippedToRoI.y1, //9 (GLfloat)rectClippedToRoI.x2, (GLfloat)rectClippedToRoI.y1, //10 (GLfloat)rod.right(), (GLfloat)rectClippedToRoI.y1, //11 (GLfloat)rod.left(), (GLfloat)rod.bottom(), //12 (GLfloat)rectClippedToRoI.x1, (GLfloat)rod.bottom(), //13 (GLfloat)rectClippedToRoI.x2, (GLfloat)rod.bottom(), //14 (GLfloat)rod.right(), (GLfloat)rod.bottom() //15 }; // GLfloat texBottom = 0; // GLfloat texTop = (GLfloat)(r.y2 - r.y1) / (GLfloat)(r.h /** r.closestPo2*/); // GLfloat texLeft = 0; // GLfloat texRight = (GLfloat)(r.x2 - r.x1) / (GLfloat)(r.w /** r.closestPo2*/); GLfloat texBottom = (GLfloat)(rectClippedToRoI.y1 - canonicalRoIRoundedToTileSize.y1) / canonicalRoIRoundedToTileSize.height(); GLfloat texTop = (GLfloat)(rectClippedToRoI.y2 - canonicalRoIRoundedToTileSize.y1) / canonicalRoIRoundedToTileSize.height(); GLfloat texLeft = (GLfloat)(rectClippedToRoI.x1 - canonicalRoIRoundedToTileSize.x1) / canonicalRoIRoundedToTileSize.width(); GLfloat texRight = (GLfloat)(rectClippedToRoI.x2 - canonicalRoIRoundedToTileSize.x1) / canonicalRoIRoundedToTileSize.width(); GLfloat renderingTextureCoordinates[32] = { texLeft, texTop, //0 texLeft, texTop, //1 texRight, texTop, //2 texRight, texTop, //3 texLeft, texTop, //4 texLeft, texTop, //5 texRight, texTop, //6 texRight, texTop, //7 texLeft, texBottom, //8 texLeft, texBottom, //9 texRight, texBottom, //10 texRight, texBottom, //11 texLeft, texBottom, // 12 texLeft, texBottom, //13 texRight, texBottom, //14 texRight, texBottom //15 }; if ( background && this->viewerTab->isCheckerboardEnabled() && (polygonMode != eDrawPolygonModeWipeRight) ) { bool isblend = GL_GPU::glIsEnabled(GL_BLEND); if (isblend) { GL_GPU::glDisable(GL_BLEND); } this->drawCheckerboardTexture(rod); if (isblend) { GL_GPU::glEnable(GL_BLEND); } } this->bindTextureAndActivateShader(textureIndex, useShader); glCheckError(GL_GPU); GL_GPU::glBindBuffer(GL_ARRAY_BUFFER, this->vboVerticesId); GL_GPU::glBufferSubData(GL_ARRAY_BUFFER, 0, 32 * sizeof(GLfloat), vertices); GL_GPU::glEnableClientState(GL_VERTEX_ARRAY); GL_GPU::glVertexPointer(2, GL_FLOAT, 0, 0); GL_GPU::glBindBuffer(GL_ARRAY_BUFFER, this->vboTexturesId); GL_GPU::glBufferSubData(GL_ARRAY_BUFFER, 0, 32 * sizeof(GLfloat), renderingTextureCoordinates); GL_GPU::glClientActiveTexture(GL_TEXTURE0); GL_GPU::glEnableClientState(GL_TEXTURE_COORD_ARRAY); GL_GPU::glTexCoordPointer(2, GL_FLOAT, 0, 0); GL_GPU::glDisableClientState(GL_COLOR_ARRAY); GL_GPU::glBindBuffer(GL_ARRAY_BUFFER, 0); GL_GPU::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->iboTriangleStripId); GL_GPU::glDrawElements(GL_TRIANGLE_STRIP, 28, GL_UNSIGNED_BYTE, 0); glCheckErrorIgnoreOSXBug(GL_GPU); GL_GPU::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL_GPU::glDisableClientState(GL_VERTEX_ARRAY); GL_GPU::glDisableClientState(GL_TEXTURE_COORD_ARRAY); glCheckError(GL_GPU); this->unbindTextureAndReleaseShader(useShader); } } // drawRenderingVAO
void AnimationModuleViewPrivate::drawCurveEditorScale() { glCheckError(GL_GPU); // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); QPointF btmLeft = curveEditorZoomContext.toZoomCoordinates(0, _publicInterface->height() - 1); QPointF topRight = curveEditorZoomContext.toZoomCoordinates(_publicInterface->width() - 1, 0); ///don't attempt to draw a scale on a widget with an invalid height/width if ( (_publicInterface->height() <= 1) || (_publicInterface->width() <= 1) ) { return; } QFontMetrics fontM = _publicInterface->fontMetrics(); const double smallestTickSizePixel = 10.; // tick size (in pixels) for alpha = 0. const double largestTickSizePixel = 500.; // tick size (in pixels) for alpha = 1. double gridR, gridG, gridB; SettingsPtr sett = appPTR->getCurrentSettings(); sett->getAnimationModuleEditorGridColor(&gridR, &gridG, &gridB); double scaleR, scaleG, scaleB; sett->getAnimationModuleEditorScaleColor(&scaleR, &scaleG, &scaleB); QColor scaleColor; scaleColor.setRgbF( Image::clamp(scaleR, 0., 1.), Image::clamp(scaleG, 0., 1.), Image::clamp(scaleB, 0., 1.) ); { GLProtectAttrib<GL_GPU> a(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT); GL_GPU::Enable(GL_BLEND); GL_GPU::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for (int axis = 0; axis < 2; ++axis) { const double rangePixel = (axis == 0) ? _publicInterface->width() : _publicInterface->height(); // AXIS-SPECIFIC const double range_min = (axis == 0) ? btmLeft.x() : btmLeft.y(); // AXIS-SPECIFIC const double range_max = (axis == 0) ? topRight.x() : topRight.y(); // AXIS-SPECIFIC const double range = range_max - range_min; double smallTickSize; bool half_tick; ticks_size(range_min, range_max, rangePixel, smallestTickSizePixel, &smallTickSize, &half_tick); int m1, m2; const int ticks_max = 1000; double offset; ticks_bounds(range_min, range_max, smallTickSize, half_tick, ticks_max, &offset, &m1, &m2); std::vector<int> ticks; ticks_fill(half_tick, ticks_max, m1, m2, &ticks); const double smallestTickSize = range * smallestTickSizePixel / rangePixel; const double largestTickSize = range * largestTickSizePixel / rangePixel; const double minTickSizeTextPixel = (axis == 0) ? fontM.width( QLatin1String("00") ) : fontM.height(); // AXIS-SPECIFIC const double minTickSizeText = range * minTickSizeTextPixel / rangePixel; for (int i = m1; i <= m2; ++i) { double value = i * smallTickSize + offset; const double tickSize = ticks[i - m1] * smallTickSize; const double alpha = ticks_alpha(smallestTickSize, largestTickSize, tickSize); glCheckError(GL_GPU); GL_GPU::Color4f(gridR, gridG, gridB, alpha); GL_GPU::Begin(GL_LINES); if (axis == 0) { GL_GPU::Vertex2f( value, btmLeft.y() ); // AXIS-SPECIFIC GL_GPU::Vertex2f( value, topRight.y() ); // AXIS-SPECIFIC } else { GL_GPU::Vertex2f(btmLeft.x(), value); // AXIS-SPECIFIC GL_GPU::Vertex2f(topRight.x(), value); // AXIS-SPECIFIC } GL_GPU::End(); glCheckErrorIgnoreOSXBug(GL_GPU); if (tickSize > minTickSizeText) { const int tickSizePixel = rangePixel * tickSize / range; const QString s = QString::number(value); const int sSizePixel = (axis == 0) ? fontM.width(s) : fontM.height(); // AXIS-SPECIFIC if (tickSizePixel > sSizePixel) { const int sSizeFullPixel = sSizePixel + minTickSizeTextPixel; double alphaText = 1.0; //alpha; if (tickSizePixel < sSizeFullPixel) { // when the text size is between sSizePixel and sSizeFullPixel, // draw it with a lower alpha alphaText *= (tickSizePixel - sSizePixel) / (double)minTickSizeTextPixel; } alphaText = std::min(alphaText, alpha); // don't draw more opaque than tcks QColor c = scaleColor; c.setAlpha(255 * alphaText); if (axis == 0) { _publicInterface->renderText(value, btmLeft.y(), s.toStdString(), c.redF(), c.greenF(), c.blueF(), c.alphaF(), Qt::AlignHCenter); } else { _publicInterface->renderText(btmLeft.x(), value, s.toStdString(), c.redF(), c.greenF(), c.blueF(), c.alphaF(), Qt::AlignVCenter); } } } } } } // GLProtectAttrib a(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT); glCheckError(GL_GPU); GL_GPU::Color4f(gridR, gridG, gridB, 1.); GL_GPU::Begin(GL_LINES); GL_GPU::Vertex2f(AXIS_MIN, 0); GL_GPU::Vertex2f(AXIS_MAX, 0); GL_GPU::Vertex2f(0, AXIS_MIN); GL_GPU::Vertex2f(0, AXIS_MAX); GL_GPU::End(); glCheckErrorIgnoreOSXBug(GL_GPU); } // drawCurveEditorScale
void CurveGui::drawCurve(int curveIndex, int curvesCount) { // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); AnimItemBasePtr item = _imp->item.lock(); if (!item) { return; } std::vector<float> vertices, exprVertices; const double widgetWidth = _imp->curveWidget->width(); KeyFrameSet keyframes; bool hasDrawnExpr = false; if (item->hasExpression(_imp->dimension, _imp->view)) { //we have no choice but to evaluate the expression at each time for (int i = 0; i < widgetWidth; ++i) { double x = _imp->curveWidget->toZoomCoordinates(i, 0).x(); double y = evaluate(true /*useExpr*/, x); exprVertices.push_back(x); exprVertices.push_back(y); } hasDrawnExpr = true; } QPointF btmLeft = _imp->curveWidget->toZoomCoordinates(0, _imp->curveWidget->height() - 1); QPointF topRight = _imp->curveWidget->toZoomCoordinates(_imp->curveWidget->width() - 1, 0); bool isPeriodic = false; std::pair<double,double> parametricRange = std::make_pair(-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()); keyframes = getInternalCurve()->getKeyFrames_mt_safe(); isPeriodic = getInternalCurve()->isCurvePeriodic(); parametricRange = getInternalCurve()->getXRange(); if ( keyframes.empty() ) { // Add a horizontal line for constant knobs, except string knobs. KnobIPtr isKnob = boost::dynamic_pointer_cast<KnobI>(item->getInternalAnimItem()); if (isKnob) { KnobStringBasePtr isString = boost::dynamic_pointer_cast<KnobStringBase>(isKnob); if (!isString) { double value = evaluate(false, 0); vertices.push_back(btmLeft.x() + 1); vertices.push_back(value); vertices.push_back(topRight.x() - 1); vertices.push_back(value); } } } else { try { double x1 = 0; double x2; bool isX1AKey = false; KeyFrame x1Key; KeyFrameSet::const_iterator lastUpperIt = keyframes.end(); while ( x1 < (widgetWidth - 1) ) { double x, y; if (!isX1AKey) { x = _imp->curveWidget->toZoomCoordinates(x1, 0).x(); y = evaluate(false, x); } else { x = x1Key.getTime(); y = x1Key.getValue(); } vertices.push_back( (float)x ); vertices.push_back( (float)y ); nextPointForSegment(x, keyframes, isPeriodic, parametricRange.first, parametricRange.second, &lastUpperIt, &x2, &x1Key, &isX1AKey); x1 = x2; } //also add the last point { double x = _imp->curveWidget->toZoomCoordinates(x1, 0).x(); double y = evaluate(false, x); vertices.push_back( (float)x ); vertices.push_back( (float)y ); } } catch (...) { } } // No Expr curve or no vertices for the curve, don't draw anything else if (exprVertices.empty() && vertices.empty()) { return; } AnimationModuleSelectionModelPtr selectionModel = item->getModel()->getSelectionModel(); assert(selectionModel); const AnimItemDimViewKeyFramesMap& selectedKeys = selectionModel->getCurrentKeyFramesSelection(); const KeyFrameWithStringSet* foundThisCurveSelectedKeys = 0; { AnimItemDimViewIndexID k; k.item = item; k.dim = _imp->dimension; k.view = _imp->view; AnimItemDimViewKeyFramesMap::const_iterator foundDimView = selectedKeys.find(k); if (foundDimView != selectedKeys.end()) { foundThisCurveSelectedKeys = &foundDimView->second; } } { GLProtectAttrib<GL_GPU> a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT); // If this is the only curve selected, draw min/max if (foundThisCurveSelectedKeys && selectedKeys.size()) { // Draw y min/max axis so the user understands why the value is clamped Curve::YRange curveYRange = getCurveYRange(); if (curveYRange.min != INT_MIN && curveYRange.min != -std::numeric_limits<double>::infinity() && curveYRange.max != INT_MAX && curveYRange.max != std::numeric_limits<double>::infinity() ) { QColor minMaxColor; minMaxColor.setRgbF(0.398979, 0.398979, 0.398979); GL_GPU::Color4d(minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), 1.); GL_GPU::Begin(GL_LINES); GL_GPU::Vertex2d(btmLeft.x(), curveYRange.min); GL_GPU::Vertex2d(topRight.x(), curveYRange.min); GL_GPU::Vertex2d(btmLeft.x(), curveYRange.max); GL_GPU::Vertex2d(topRight.x(), curveYRange.max); GL_GPU::End(); GL_GPU::Color4d(1., 1., 1., 1.); double xText = _imp->curveWidget->toZoomCoordinates(10, 0).x(); _imp->curveWidget->renderText( xText, curveYRange.min, tr("min").toStdString(), minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), minMaxColor.alphaF()); _imp->curveWidget->renderText( xText, curveYRange.max, tr("max").toStdString(), minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), minMaxColor.alphaF()); } } GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]); GL_GPU::PointSize(_imp->lineWidth); GL_GPU::Enable(GL_BLEND); GL_GPU::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_GPU::Enable(GL_LINE_SMOOTH); GL_GPU::Hint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); GL_GPU::LineWidth(1.5); glCheckError(GL_GPU); if (hasDrawnExpr) { drawLineStrip(exprVertices, btmLeft, topRight); GL_GPU::LineStipple(2, 0xAAAA); GL_GPU::Enable(GL_LINE_STIPPLE); } drawLineStrip(vertices, btmLeft, topRight); if (hasDrawnExpr) { GL_GPU::Disable(GL_LINE_STIPPLE); } glCheckErrorIgnoreOSXBug(GL_GPU); //render the name of the curve GL_GPU::Color4f(1.f, 1.f, 1.f, 1.f); double interval = ( topRight.x() - btmLeft.x() ) / (double)curvesCount; double textX = _imp->curveWidget->toZoomCoordinates(15, 0).x() + interval * (double)curveIndex; double textY; CurvePtr internalCurve = _imp->internalCurve.lock(); QString curveName = getName(); QColor thisColor; thisColor.setRgbF(Image::clamp(_imp->color[0], 0., 1.), Image::clamp(_imp->color[1], 0., 1.), Image::clamp(_imp->color[2], 0., 1.)); try { // Use expression to place the text if the curve is not animated textY = evaluate(internalCurve && !internalCurve->isAnimated(), textX); } catch (...) { // if it fails attempt without expression, this will most likely return a constant value textY = evaluate(false /*useExpression*/, textX); } if ( ( textX >= btmLeft.x() ) && ( textX <= topRight.x() ) && ( textY >= btmLeft.y() ) && ( textY <= topRight.y() ) ) { _imp->curveWidget->renderText( textX, textY, curveName.toStdString(), thisColor.redF(), thisColor.greenF(), thisColor.blueF(), thisColor.alphaF()); } GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]); //draw keyframes GL_GPU::PointSize(7.f); GL_GPU::Enable(GL_POINT_SMOOTH); KeyFrameWithStringSet::const_iterator foundSelectedKey; if (foundThisCurveSelectedKeys) { foundSelectedKey = foundThisCurveSelectedKeys->end(); } for (KeyFrameSet::const_iterator k = keyframes.begin(); k != keyframes.end(); ++k) { const KeyFrame & key = (*k); // Do not draw keyframes out of range if ( ( key.getTime() < btmLeft.x() ) || ( key.getTime() > topRight.x() ) || ( key.getValue() < btmLeft.y() ) || ( key.getValue() > topRight.y() ) ) { continue; } GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]); bool drawKeySelected = false; if (foundThisCurveSelectedKeys) { KeyFrameWithStringSet::const_iterator start = foundSelectedKey == foundThisCurveSelectedKeys->end() ? foundThisCurveSelectedKeys->begin() : foundSelectedKey; foundSelectedKey = std::find_if(start, foundThisCurveSelectedKeys->end(), KeyFrameWithStringTimePredicate(key.getTime())); drawKeySelected = foundSelectedKey != foundThisCurveSelectedKeys->end(); if (!drawKeySelected) { // Also draw the keyframe as selected if it is inside the selection rectangle (but not yet selected) RectD selectionRect = _imp->curveWidget->getSelectionRectangle(); drawKeySelected |= _imp->curveWidget->_imp->eventTriggeredFromCurveEditor && (!selectionRect.isNull() && selectionRect.contains(key.getTime(), key.getValue())); } } // If the key is selected change its color if (drawKeySelected) { GL_GPU::Color4f(0.8f, 0.8f, 0.8f, 1.f); } RectD keyframeBbox = _imp->curveWidget->_imp->getKeyFrameBoundingRectCanonical(_imp->curveWidget->_imp->curveEditorZoomContext, key.getTime(), key.getValue()); // draw texture of the keyframe { AnimationModuleViewPrivate::KeyframeTexture texType = AnimationModuleViewPrivate::kfTextureFromKeyframeType( key.getInterpolation(), drawKeySelected); if (texType != AnimationModuleViewPrivate::kfTextureNone) { _imp->curveWidget->_imp->drawTexturedKeyframe(texType, keyframeBbox, false /*drawdimed*/); } } // Draw tangents if not constant bool drawTangents = drawKeySelected && internalCurve->isYComponentMovable() && (key.getInterpolation() != eKeyframeTypeConstant); if (drawTangents) { QFontMetrics m( _imp->curveWidget->font()); // If interpolation is not free and not broken display with dashes the tangents lines if ( (key.getInterpolation() != eKeyframeTypeFree) && (key.getInterpolation() != eKeyframeTypeBroken) ) { GL_GPU::LineStipple(2, 0xAAAA); GL_GPU::Enable(GL_LINE_STIPPLE); } QPointF leftTanPos, rightTanPos; _imp->curveWidget->getKeyTangentPoints(k, keyframes, &leftTanPos, &rightTanPos); // Draw the derivatives lines GL_GPU::Begin(GL_LINES); GL_GPU::Color4f(1., 0.35, 0.35, 1.); GL_GPU::Vertex2f( leftTanPos.x(), leftTanPos.y() ); GL_GPU::Vertex2f(key.getTime(), key.getValue()); GL_GPU::Vertex2f(key.getTime(), key.getValue()); GL_GPU::Vertex2f( rightTanPos.x(), rightTanPos.y()); GL_GPU::End(); if ( (key.getInterpolation() != eKeyframeTypeFree) && (key.getInterpolation() != eKeyframeTypeBroken) ) { GL_GPU::Disable(GL_LINE_STIPPLE); } // Draw the tangents handles GL_GPU::Begin(GL_POINTS); GL_GPU::Vertex2f( leftTanPos.x(), leftTanPos.y() ); GL_GPU::Vertex2f( rightTanPos.x(), rightTanPos.y()); GL_GPU::End(); // If only one keyframe is selected, also draw the coordinates if (selectedKeys.size() == 1 && foundThisCurveSelectedKeys && foundThisCurveSelectedKeys->size() == 1) { double rounding = std::pow(10., CURVEWIDGET_DERIVATIVE_ROUND_PRECISION); QString leftDerivStr = QString::fromUtf8("l: %1").arg(std::floor( (key.getLeftDerivative() * rounding) + 0.5 ) / rounding); QString rightDerivStr = QString::fromUtf8("r: %1").arg(std::floor( (key.getRightDerivative() * rounding) + 0.5 ) / rounding); double yLeftWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, leftTanPos.y()).y(); yLeftWidgetCoord += (m.height() + 4); double yRightWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, rightTanPos.y()).y(); yRightWidgetCoord += (m.height() + 4); GL_GPU::Color4f(1., 1., 1., 1.); glCheckFramebufferError(GL_GPU); _imp->curveWidget->renderText( leftTanPos.x(), _imp->curveWidget->toZoomCoordinates(0, yLeftWidgetCoord).y(), leftDerivStr.toStdString(), 0.9, 0.9, 0.9, 1.); _imp->curveWidget->renderText( rightTanPos.x(), _imp->curveWidget->toZoomCoordinates(0, yRightWidgetCoord).y(), rightDerivStr.toStdString(), 0.9, 0.9, 0.9, 1.); QString coordStr = QString::fromUtf8("x: %1, y: %2"); coordStr = coordStr.arg(key.getTime()).arg(key.getValue()); double yWidgetCoord = _imp->curveWidget->toWidgetCoordinates( 0, key.getValue() ).y(); yWidgetCoord += (m.height() + 4); GL_GPU::Color4f(1., 1., 1., 1.); glCheckFramebufferError(GL_GPU); _imp->curveWidget->renderText( key.getTime(), _imp->curveWidget->toZoomCoordinates(0, yWidgetCoord).y(), coordStr.toStdString(), 0.9, 0.9, 0.9, 1.); } } // drawTangents } // for (KeyFrameSet::const_iterator k = keyframes.begin(); k != keyframes.end(); ++k) { } // GLProtectAttrib(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT); glCheckError(GL_GPU); } // drawCurve
void TextRenderer::renderText(float x, float y, float scalex, float scaley, const QString &text, const QColor &color, const QFont &font) const { glCheckError(); boost::shared_ptr<TextRendererPrivate> p; FontRenderers::iterator it = _imp->renderers.find(font); if ( it != _imp->renderers.end() ) { p = (*it).second; } else { p = boost::shared_ptr<TextRendererPrivate>( new TextRendererPrivate(font) ); _imp->renderers[font] = p; } assert(p); GLuint savedTexture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&savedTexture); { GLProtectAttrib a(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_TRANSFORM_BIT); GLProtectMatrix pr(GL_MODELVIEW); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); GLuint texture = 0; glTranslatef(x, y, 0); glColor4f( color.redF(), color.greenF(), color.blueF(), color.alphaF() ); for (int i = 0; i < text.length(); ++i) { CharBitmap *c = p->createCharacter(text[i]); if (!c) { continue; } if (texture != c->texID) { texture = c->texID; glBindTexture(GL_TEXTURE_2D, texture); assert( glIsTexture(texture) ); } glCheckError(); glBegin(GL_QUADS); glTexCoord2f(c->xTexCoords[0], c->yTexCoords[0]); glVertex2f(0, 0); glTexCoord2f(c->xTexCoords[1], c->yTexCoords[0]); glVertex2f(c->w * scalex, 0); glTexCoord2f(c->xTexCoords[1], c->yTexCoords[1]); glVertex2f(c->w * scalex, c->h * scaley); glTexCoord2f(c->xTexCoords[0], c->yTexCoords[1]); glVertex2f(0, c->h * scaley); glEnd(); glCheckErrorIgnoreOSXBug(); glTranslatef(c->w * scalex, 0, 0); glCheckError(); } } // GLProtectAttrib a(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_TRANSFORM_BIT); glBindTexture(GL_TEXTURE_2D, savedTexture); glCheckError(); } // renderText