void ViewerDisplayScheduler::timelineGoTo(TimeValue time) { ViewerNodePtr isViewer = getOutputNode()->isEffectViewerNode(); assert(isViewer); isViewer->getTimeline()->seekFrame(time, true, isViewer, eTimelineChangeReasonPlaybackSeek); }
ViewerTab::~ViewerTab() { Gui* gui = getGui(); if (gui) { NodeGraph* graph = 0; ViewerNodePtr internalNode = getInternalNode(); ViewerInstancePtr viewerNode = internalNode ? internalNode->getInternalViewerNode() : ViewerInstancePtr(); if (viewerNode) { NodeCollectionPtr collection = viewerNode->getNode()->getGroup(); if (collection) { NodeGroupPtr isGrp = toNodeGroup(collection); if (isGrp) { NodeGraphI* graph_i = isGrp->getNodeGraph(); if (graph_i) { graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); } } else { graph = gui->getNodeGraph(); } } internalNode->invalidateUiContext(); } else { graph = gui->getNodeGraph(); } assert(graph); GuiAppInstancePtr app = gui->getApp(); if ( app && !app->isClosing() && graph && (graph->getLastSelectedViewer() == this) ) { graph->setLastSelectedViewer(0); } } _imp->nodesContext.clear(); }
void ViewerGL::Implementation::activateShaderRGB(int texIndex) { // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); // we assume that: // - 8-bits textures are stored non-linear and must be displayer as is // - floating-point textures are linear and must be decompressed according to the given lut if ( !shaderRGB->bind() ) { qDebug() << "Error when binding shader" << qPrintable( shaderRGB->log() ); } ViewerNodePtr node = _this->getInternalNode(); double gain = node->getGain(); double gamma = node->getGamma(); shaderRGB->setUniformValue("Tex", 0); shaderRGB->setUniformValue("gain", (float)gain); shaderRGB->setUniformValue("offset", (float)displayTextures[texIndex].offset); shaderRGB->setUniformValue("lut", (GLint)displayingImageLut); shaderRGB->setUniformValue("gamma", (float)gamma); }
void ViewerTab::connectToBInput(int inputNb) { ViewerNodePtr internalNode = getInternalNode(); if (internalNode) { internalNode->connectInputToIndex(inputNb, 1); } }
void ViewerTab::updateZoomComboBox(int value) { assert(value > 0); QString str = QString::number(value); str.append( QLatin1Char('%') ); ViewerNodePtr internalNode = getInternalNode(); if (internalNode) { internalNode->setZoomComboBoxText(str.toStdString()); } }
void ViewerDisplayScheduler::onRenderFailed(ActionRetCodeEnum status) { // Upon failure clear the viewer to black. The node that failed should have posted a persistent message. if (status == eActionStatusAborted) { // When aborted, do not clear the viewer return; } ViewerNodePtr effect = getOutputNode()->isEffectViewerNode(); effect->disconnectViewer(); }
ActionRetCodeEnum ViewerDisplayScheduler::createFrameRenderResultsGeneric(const ViewerNodePtr& viewer, const TreeRenderQueueProviderPtr& provider, TimeValue time, bool isPlayback, const RotoStrokeItemPtr& activeDrawingStroke, const std::vector<ViewIdx>& viewsToRender, bool enableRenderStats, RenderFrameResultsContainerPtr* future) { // A global statistics object for this frame render if requested RenderStatsPtr stats; if (enableRenderStats) { stats.reset( new RenderStats(enableRenderStats) ); } // Get parameters from the viewport bool fullFrameProcessing = viewer->isFullFrameProcessingEnabled(); bool draftModeEnabled = viewer->getApp()->isDraftRenderEnabled(); unsigned int mipMapLevel = getViewerMipMapLevel(viewer, draftModeEnabled, fullFrameProcessing); bool byPassCache = viewer->isRenderWithoutCacheEnabledAndTurnOff(); ViewerCompositingOperatorEnum viewerBlend = viewer->getCurrentOperator(); bool viewerBEqualsViewerA = viewer->getCurrentAInput() == viewer->getCurrentBInput(); // Create the global results object ViewerRenderFrameResultsContainerPtr results(new ViewerRenderFrameResultsContainer(provider)); *future = results; results->time = time; results->recenterViewer = viewer->getViewerCenterPoint(&results->viewerCenter); std::list<RectD> rois; if (!viewer->isDoingPartialUpdates()) { rois.push_back(RectD()); } else { // If the viewer is doing partial updates (i.e: during tracking we only update the markers areas) // Then we launch multiple renders over the partial areas std::list<RectD> partialUpdates = viewer->getPartialUpdateRects(); for (std::list<RectD>::const_iterator it = partialUpdates.begin(); it != partialUpdates.end(); ++it) { if (!it->isNull()) { rois.push_back(*it); } } } for (std::list<RectD>::const_iterator it = rois.begin(); it != rois.end(); ++it) { // Render all requested views for (std::size_t view_i = 0; view_i < viewsToRender.size(); ++view_i) { ActionRetCodeEnum stat = createFrameRenderResultsForView(viewer, provider, results, viewsToRender[view_i], stats, isPlayback, it->isNull() ? 0 : &(*it), mipMapLevel, viewerBlend, byPassCache, draftModeEnabled, fullFrameProcessing, viewerBEqualsViewerA, activeDrawingStroke); if (isFailureRetCode(stat)) { return stat; } } // for each view } return eActionStatusOK; } // createFrameRenderResultsGeneric
void ViewerDisplayScheduler::getFrameRangeToRender(TimeValue &first, TimeValue &last) const { ViewerNodePtr isViewer = getOutputNode()->isEffectViewerNode(); ViewerNodePtr leadViewer = isViewer->getApp()->getLastViewerUsingTimeline(); ViewerNodePtr v = leadViewer ? leadViewer : isViewer; assert(v); int left, right; v->getTimelineBounds(&left, &right); first = TimeValue(left); last = TimeValue(right); }
bool ViewerDisplayScheduler::processFramesResults(const ViewerNodePtr& viewer,const RenderFrameResultsContainerPtr& results) { if ( results->frames.empty() ) { viewer->redrawViewer(); return false; } ViewerRenderFrameResultsContainerPtr viewerResults = boost::dynamic_pointer_cast<ViewerRenderFrameResultsContainer>(results); assert(viewerResults); bool didSomething = false; for (std::list<RenderFrameSubResultPtr>::const_iterator it = viewerResults->frames.begin(); it != viewerResults->frames.end(); ++it) { ViewerRenderFrameSubResult* viewerObject = dynamic_cast<ViewerRenderFrameSubResult*>(it->get()); assert(viewerObject); ViewerNode::UpdateViewerArgs args; args.time = results->time; args.view = (*it)->view; args.type = viewerObject->textureTransferType; args.recenterViewer = viewerResults->recenterViewer; args.viewerCenter = viewerResults->viewerCenter; for (int i = 0; i < 2; ++i) { const PerViewerInputRenderData& inputData = viewerObject->perInputsData[i]; ViewerNode::UpdateViewerArgs::TextureUpload upload; upload.image = inputData.viewerProcessImage; upload.colorPickerImage = inputData.colorPickerImage; upload.colorPickerInputImage = inputData.colorPickerInputImage; upload.viewerProcessImageKey = inputData.viewerProcessImageKey; if (inputData.retCode == eActionStatusAborted || (inputData.retCode == eActionStatusOK && !upload.image)) { // If aborted or no image was rendered but the result was OK (one of the reasons could be the caller requested a RoI outside of the bounds of the image), don't transfer any texture, just redraw the viewer. continue; } args.viewerUploads[i].push_back(upload); } if (!args.viewerUploads[0].empty() || !args.viewerUploads[1].empty()) { viewer->updateViewer(args); didSomething = true; } } viewer->redrawViewer(); return didSomething; } // processFramesResults
NATRON_NAMESPACE_ENTER NATRON_NAMESPACE_ANONYMOUS_ENTER static unsigned getViewerMipMapLevel(const ViewerNodePtr& viewer, bool draftModeEnabled, bool fullFrameProcessing) { if (fullFrameProcessing) { return 0; } unsigned int mipMapLevel = 0; const double zoomFactor = viewer->getUIZoomFactor(); int downcale_i = viewer->getDownscaleMipMapLevelKnobIndex(); assert(downcale_i >= 0); if (downcale_i > 0) { mipMapLevel = downcale_i; } else { mipMapLevel = viewer->getMipMapLevelFromZoomFactor(); } // If draft mode is enabled, compute the mipmap level according to the auto-proxy setting in the preferences if ( draftModeEnabled && appPTR->getCurrentSettings()->isAutoProxyEnabled() ) { unsigned int autoProxyLevel = appPTR->getCurrentSettings()->getAutoProxyMipMapLevel(); if (zoomFactor > 1) { //Decrease draft mode at each inverse mipmaplevel level taken unsigned int invLevel = Image::getLevelFromScale(1. / zoomFactor); if (invLevel < autoProxyLevel) { autoProxyLevel -= invLevel; } else { autoProxyLevel = 0; } } mipMapLevel = (unsigned int)std::max( (int)mipMapLevel, (int)autoProxyLevel ); } return mipMapLevel; } // getViewerMipMapLevel
void ViewerTab::onTimeLineTimeChanged(SequenceTime time, int reason) { Gui* gui = getGui(); if (!gui) { return; } ViewerNodePtr node = _imp->viewerNode.lock(); ViewerInstancePtr viewerNode = node->getInternalViewerNode(); if ((TimelineChangeReasonEnum)reason != eTimelineChangeReasonPlaybackSeek) { node->getCurrentFrameKnob()->setValue(time, ViewSetSpec::current(), DimIdx(0), eValueChangedReasonPluginEdited); } GuiAppInstancePtr app = gui->getApp(); if ( app && _imp->timeLineGui->getTimeline() != app->getTimeLine() ) { viewerNode->renderCurrentFrame(true); } }
void ViewerTab::abortViewersAndRefresh() { Gui* gui = getGui(); if (!gui) { return; } const std::list<ViewerTab*> & activeNodes = gui->getViewersList(); for (std::list<ViewerTab*>::const_iterator it = activeNodes.begin(); it != activeNodes.end(); ++it) { ViewerNodePtr viewer = (*it)->getInternalNode(); if (viewer) { ViewerInstancePtr instance = viewer->getInternalViewerNode(); if (instance) { RenderEnginePtr engine = instance->getRenderEngine(); if ( engine ) { engine->abortRenderingAutoRestart(); engine->renderCurrentFrame(false, true); } } } } }
bool ViewerTab::eventFilter(QObject *target, QEvent* e) { if (e->type() == QEvent::MouseButtonPress) { Gui* gui = getGui(); if (gui) { GuiAppInstancePtr app = gui->getApp(); if (app) { ViewerNodePtr viewerNode = _imp->viewerNode.lock(); if (viewerNode) { NodeGuiIPtr nodegui_i = viewerNode->getNode()->getNodeGui(); assert(nodegui_i); NodeGuiPtr nodegui = boost::dynamic_pointer_cast<NodeGui>(nodegui_i); gui->selectNode(nodegui); } } } } return QWidget::eventFilter(target, e); }
ViewerGL::Implementation::WipePolygonEnum ViewerGL::Implementation::getWipePolygon(const RectD & texRectClipped, bool rightPlane, QPolygonF * polygonPoints) const { ///Compute a second point on the plane separator line ///we don't really care how far it is from the center point, it just has to be on the line ViewerNodePtr node = _this->getViewerTab()->getInternalNode(); QPointF center = node->getWipeCenter(); double angle = node->getWipeAngle(); ///extrapolate the line to the maximum size of the RoD so we're sure the line ///intersection algorithm works double maxSize = std::max(texRectClipped.x2 - texRectClipped.x1, texRectClipped.y2 - texRectClipped.y1) * 10000.; double xmax, ymax; xmax = std::cos(angle + M_PI_2) * maxSize; ymax = std::sin(angle + M_PI_2) * maxSize; // first, compute wether the whole rectangle is on one side of the wipe const QPointF firstPoint ( center.x() + (rightPlane ? xmax : -xmax), center.y() + (rightPlane ? ymax : -ymax) ); const QPointF secondPoint( center.x() + (rightPlane ? -xmax : xmax), center.y() + (rightPlane ? -ymax : ymax) ); double crossProd11 = ( ( secondPoint.x() - center.x() ) * ( texRectClipped.y1 - center.y() ) - ( secondPoint.y() - center.y() ) * ( texRectClipped.x1 - center.x() ) ); double crossProd12 = ( ( secondPoint.x() - center.x() ) * ( texRectClipped.y2 - center.y() ) - ( secondPoint.y() - center.y() ) * ( texRectClipped.x1 - center.x() ) ); double crossProd21 = ( ( secondPoint.x() - center.x() ) * ( texRectClipped.y1 - center.y() ) - ( secondPoint.y() - center.y() ) * ( texRectClipped.x2 - center.x() ) ); double crossProd22 = ( ( secondPoint.x() - center.x() ) * ( texRectClipped.y2 - center.y() ) - ( secondPoint.y() - center.y() ) * ( texRectClipped.x2 - center.x() ) ); polygonPoints->clear(); // if all cross products have the same sign, the rectangle is on one side if ( (crossProd11 >= 0) && (crossProd12 >= 0) && (crossProd21 >= 0) && (crossProd22 >= 0) ) { return ViewerGL::Implementation::eWipePolygonFull; } if ( (crossProd11 <= 0) && (crossProd12 <= 0) && (crossProd21 <= 0) && (crossProd22 <= 0) ) { return ViewerGL::Implementation::eWipePolygonEmpty; } // now go through all four corners: // - if the cross product is positive, the corner must be inserted // - if the cross-product changes sign then the intersection must be inserted const QLineF inter(firstPoint, secondPoint); if (crossProd11 >= 0) { *polygonPoints << QPointF(texRectClipped.x1, texRectClipped.y1); } if (crossProd11 * crossProd21 < 0) { QLineF e(texRectClipped.x1, texRectClipped.y1, texRectClipped.x2, texRectClipped.y1); QPointF p; QLineF::IntersectType t = inter.intersect(e, &p); if (t == QLineF::BoundedIntersection) { *polygonPoints << p; } } if (crossProd21 >= 0) { *polygonPoints << QPointF(texRectClipped.x2, texRectClipped.y1); } if (crossProd21 * crossProd22 < 0) { QLineF e(texRectClipped.x2, texRectClipped.y1, texRectClipped.x2, texRectClipped.y2); QPointF p; QLineF::IntersectType t = inter.intersect(e, &p); if (t == QLineF::BoundedIntersection) { *polygonPoints << p; } } if (crossProd22 >= 0) { *polygonPoints << QPointF(texRectClipped.x2, texRectClipped.y2); } if (crossProd22 * crossProd12 < 0) { QLineF e(texRectClipped.x2, texRectClipped.y2, texRectClipped.x1, texRectClipped.y2); QPointF p; QLineF::IntersectType t = inter.intersect(e, &p); if (t == QLineF::BoundedIntersection) { *polygonPoints << p; } } if (crossProd12 >= 0) { *polygonPoints << QPointF(texRectClipped.x1, texRectClipped.y2); } if (crossProd12 * crossProd11 < 0) { QLineF e(texRectClipped.x1, texRectClipped.y2, texRectClipped.x1, texRectClipped.y1); QPointF p; QLineF::IntersectType t = inter.intersect(e, &p); if (t == QLineF::BoundedIntersection) { *polygonPoints << p; } } return ViewerGL::Implementation::eWipePolygonPartial; } // getWipePolygon
// 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() ); ///the texture rectangle in image coordinates. The values in it are multiples of tile size. /// const RectI &textureBounds = this->displayTextures[textureIndex].texture->getBounds(); //const RectD& originalCanonicalRoI = this->displayTextures[textureIndex].originalCanonicalRoi; ///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 = this->displayTextures[textureIndex].pixelAspectRatio; RectD canonicalRoIRoundedToTileSize; textureBounds.toCanonical_noClipping(mipMapLevel, par /*, rod*/, &canonicalRoIRoundedToTileSize); ///the RoD of the image in canonical coords. RectD rod = _this->getRoD(textureIndex); ViewerNodePtr internalNode = _this->getViewerTab()->getInternalNode(); bool clipToDisplayWindow = internalNode->isClipToFormatEnabled(); RectD rectClippedToRoI(canonicalRoIRoundedToTileSize); rectClippedToRoI.intersect(rod, &rectClippedToRoI); if (clipToDisplayWindow) { rod.intersect(this->displayTextures[textureIndex].format, &rod); rectClippedToRoI.intersect(this->displayTextures[textureIndex].format, &rectClippedToRoI); } //if user RoI is enabled, clip the rod to that roi bool userRoiEnabled = internalNode->isUserRoIEnabled(); ////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) { RectD userRoI = internalNode->getUserRoI(); //if the userRoI isn't intersecting the rod, just don't render anything if ( !rod.intersect(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); assert(displayTextures[textureIndex].texture); GL_GPU::ActiveTexture(GL_TEXTURE0); GL_GPU::GetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&prevBoundTexture); GL_GPU::BindTexture( GL_TEXTURE_2D, displayTextures[textureIndex].texture->getTexID() ); GL_GPU::Begin(GL_POLYGON); for (int i = 0; i < polygonTexCoords.size(); ++i) { const QPointF & tCoord = polygonTexCoords[i]; const QPointF & vCoord = polygonPoints[i]; GL_GPU::TexCoord2d( tCoord.x(), tCoord.y() ); GL_GPU::Vertex2d( vCoord.x(), vCoord.y() ); } GL_GPU::End(); GL_GPU::BindTexture( GL_TEXTURE_2D, prevBoundTexture); } 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 = (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 && internalNode->isCheckerboardEnabled() && (polygonMode != eDrawPolygonModeWipeRight) ) { bool isblend = GL_GPU::IsEnabled(GL_BLEND); if (isblend) { GL_GPU::Disable(GL_BLEND); } this->drawCheckerboardTexture(rod); if (isblend) { GL_GPU::Enable(GL_BLEND); } } assert(displayTextures[textureIndex].texture); GL_GPU::ActiveTexture(GL_TEXTURE0); GL_GPU::GetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&prevBoundTexture); GL_GPU::BindTexture( GL_TEXTURE_2D, displayTextures[textureIndex].texture->getTexID() ); glCheckError(GL_GPU); GL_GPU::BindBuffer(GL_ARRAY_BUFFER, this->vboVerticesId); GL_GPU::BufferSubData(GL_ARRAY_BUFFER, 0, 32 * sizeof(GLfloat), vertices); GL_GPU::EnableClientState(GL_VERTEX_ARRAY); GL_GPU::VertexPointer(2, GL_FLOAT, 0, 0); GL_GPU::BindBuffer(GL_ARRAY_BUFFER, this->vboTexturesId); GL_GPU::BufferSubData(GL_ARRAY_BUFFER, 0, 32 * sizeof(GLfloat), renderingTextureCoordinates); GL_GPU::ClientActiveTexture(GL_TEXTURE0); GL_GPU::EnableClientState(GL_TEXTURE_COORD_ARRAY); GL_GPU::TexCoordPointer(2, GL_FLOAT, 0, 0); GL_GPU::DisableClientState(GL_COLOR_ARRAY); GL_GPU::BindBuffer(GL_ARRAY_BUFFER, 0); GL_GPU::BindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->iboTriangleStripId); GL_GPU::DrawElements(GL_TRIANGLE_STRIP, 28, GL_UNSIGNED_BYTE, 0); glCheckErrorIgnoreOSXBug(GL_GPU); GL_GPU::BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL_GPU::DisableClientState(GL_VERTEX_ARRAY); GL_GPU::DisableClientState(GL_TEXTURE_COORD_ARRAY); GL_GPU::BindTexture( GL_TEXTURE_2D, prevBoundTexture); glCheckError(GL_GPU); } } // drawRenderingVAO
void Gui::removeViewerTab(ViewerTab* tab, bool initiatedFromNode, bool deleteData) { assert(tab); unregisterTab(tab); if (tab == _imp->_activeViewer) { _imp->_activeViewer = 0; } NodeGraph* graph = 0; NodeGroupPtr isGrp; NodeCollectionPtr collection; if ( tab->getInternalNode() && tab->getInternalNode()->getNode() ) { NodeCollectionPtr collection = tab->getInternalNode()->getNode()->getGroup(); isGrp = toNodeGroup(collection); } if (isGrp) { NodeGraphI* graph_i = isGrp->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); } else { graph = getNodeGraph(); } assert(graph); if (!graph) { throw std::logic_error(""); } ViewerTab* lastSelectedViewer = graph->getLastSelectedViewer(); if (lastSelectedViewer == tab) { bool foundOne = false; NodesList nodes; if (collection) { nodes = collection->getNodes(); } for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { ViewerNodePtr isViewer = (*it)->isEffectViewerNode(); if ( !isViewer || ( isViewer == tab->getInternalNode() ) || !(*it)->isActivated() ) { continue; } OpenGLViewerI* viewerI = isViewer->getUiContext(); assert(viewerI); ViewerGL* glViewer = dynamic_cast<ViewerGL*>(viewerI); assert(glViewer); if (glViewer) { graph->setLastSelectedViewer( glViewer->getViewerTab() ); } foundOne = true; break; } if (!foundOne) { graph->setLastSelectedViewer(0); } } ViewerNodePtr viewerNode = tab->getInternalNode(); ViewerInstancePtr internalViewer; if (viewerNode) { internalViewer = viewerNode->getInternalViewerNode(); } if (internalViewer) { internalViewer->abortAnyEvaluation(); if (getApp()->getLastViewerUsingTimeline() == internalViewer) { getApp()->discardLastViewerUsingTimeline(); } } if (!initiatedFromNode) { assert(_imp->_nodeGraphArea); ///call the deleteNode which will call this function again when the node will be deactivated. NodePtr internalNode = tab->getInternalNode()->getNode(); NodeGuiIPtr guiI = internalNode->getNodeGui(); NodeGuiPtr gui = boost::dynamic_pointer_cast<NodeGui>(guiI); assert(gui); NodeGraphI* graph_i = internalNode->getGroup()->getNodeGraph(); assert(graph_i); NodeGraph* graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); if (graph) { graph->removeNode(gui); } } else { tab->hide(); TabWidget* container = dynamic_cast<TabWidget*>( tab->parentWidget() ); if (container) { container->removeTab(tab, false); } if (deleteData) { QMutexLocker l(&_imp->_viewerTabsMutex); std::list<ViewerTab*>::iterator it = std::find(_imp->_viewerTabs.begin(), _imp->_viewerTabs.end(), tab); if ( it != _imp->_viewerTabs.end() ) { _imp->_viewerTabs.erase(it); } tab->notifyGuiClosingPublic(); tab->deleteLater(); } } Q_EMIT viewersChanged(); } // Gui::removeViewerTab
void ViewerTab::keyPressEvent(QKeyEvent* e) { ViewerNodePtr internalNode = getInternalNode(); if (!internalNode || !internalNode->getNode()) { return; } //qDebug() << "ViewerTab::keyPressed:" << e->text() << "modifiers:" << e->modifiers(); Gui* gui = getGui(); if (gui) { gui->setActiveViewer(this); } bool accept = true; Qt::KeyboardModifiers modifiers = e->modifiers(); Qt::Key key = (Qt::Key)Gui::handleNativeKeys( e->key(), e->nativeScanCode(), e->nativeVirtualKey() ); double scale = 1. / ( 1 << _imp->viewer->getCurrentRenderScale() ); if ( e->isAutoRepeat() && notifyOverlaysKeyRepeat(RenderScale(scale), e) ) { update(); } else if ( notifyOverlaysKeyDown(RenderScale(scale), e) ) { update(); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput1, modifiers, key) ) { connectToAInput(0); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput2, modifiers, key) ) { connectToAInput(1); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput3, modifiers, key) ) { connectToAInput(2); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput4, modifiers, key) ) { connectToAInput(3); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput5, modifiers, key) ) { connectToAInput(4); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput6, modifiers, key) ) { connectToAInput(5); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput7, modifiers, key) ) { connectToAInput(6); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput8, modifiers, key) ) { connectToAInput(7); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput9, modifiers, key) ) { connectToAInput(8); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerToInput10, modifiers, key) ) { connectToAInput(9); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput1, modifiers, key) ) { connectToBInput(0); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput2, modifiers, key) ) { connectToBInput(1); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput3, modifiers, key) ) { connectToBInput(2); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput4, modifiers, key) ) { connectToBInput(3); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput5, modifiers, key) ) { connectToBInput(4); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput6, modifiers, key) ) { connectToBInput(5); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput7, modifiers, key) ) { connectToBInput(6); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput8, modifiers, key) ) { connectToBInput(7); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput9, modifiers, key) ) { connectToBInput(8); } else if ( isKeybind(kShortcutGroupGlobal, kShortcutIDActionConnectViewerBToInput10, modifiers, key) ) { connectToBInput(9); } else if (key == Qt::Key_Escape) { _imp->viewer->s_selectionCleared(); update(); } else { accept = false; } if (accept) { takeClickFocus(); e->accept(); } else { handleUnCaughtKeyPressEvent(e); QWidget::keyPressEvent(e); } } // keyPressEvent
NATRON_NAMESPACE_ENTER ViewerTab::ViewerTab(const std::string& scriptName, const std::list<NodeGuiPtr> & existingNodesContext, const std::list<NodeGuiPtr>& activePluginsContext, Gui* gui, const NodeGuiPtr& node_ui, QWidget* parent) : QWidget(parent) , PanelWidget(scriptName, this, gui) , _imp( new ViewerTabPrivate(this, node_ui) ) { ViewerNodePtr node = node_ui->getNode()->isEffectViewerNode(); installEventFilter(this); setMouseTracking(true); NodePtr internalNode = node->getNode(); QObject::connect( internalNode.get(), SIGNAL(scriptNameChanged(QString)), this, SLOT(onInternalNodeScriptNameChanged(QString)) ); QObject::connect( internalNode.get(), SIGNAL(labelChanged(QString,QString)), this, SLOT(onInternalNodeLabelChanged(QString,QString)) ); QObject::connect( node.get(), SIGNAL(internalViewerCreated()), this, SLOT(onInternalViewerCreated())); _imp->mainLayout = new QVBoxLayout(this); setLayout(_imp->mainLayout); _imp->mainLayout->setSpacing(0); _imp->mainLayout->setContentsMargins(0, 0, 0, 0); QFontMetrics fm(font(), 0); _imp->viewerContainer = new QWidget(this); _imp->viewerLayout = new QHBoxLayout(_imp->viewerContainer); _imp->viewerLayout->setContentsMargins(0, 0, 0, 0); _imp->viewerLayout->setSpacing(0); _imp->viewerSubContainer = new QWidget(_imp->viewerContainer); _imp->viewerSubContainerLayout = new QVBoxLayout(_imp->viewerSubContainer); _imp->viewerSubContainerLayout->setContentsMargins(0, 0, 0, 0); _imp->viewerSubContainerLayout->setSpacing(1); // Info bars QString inputNames[2] = { QString::fromUtf8("A:"), QString::fromUtf8("B:") }; bool infobarvisible = node->isInfoBarVisible(); for (int i = 0; i < 2; ++i) { _imp->infoWidget[i] = new InfoViewerWidget(inputNames[i], this); } // Viewer _imp->viewer = new ViewerGL(this); GuiAppInstancePtr app = gui->getApp(); // Init viewer to project format { Format projectFormat; app->getProject()->getProjectDefaultFormat(&projectFormat); RectD canonicalFormat = projectFormat.toCanonicalFormat(); for (int i = 0; i < 2; ++i) { _imp->viewer->setInfoViewer(_imp->infoWidget[i], i); _imp->viewer->setRegionOfDefinition(canonicalFormat, projectFormat.getPixelAspectRatio(), i); setInfoBarAndViewerResolution(projectFormat, canonicalFormat, projectFormat.getPixelAspectRatio(), i); } _imp->viewer->resetWipeControls(); } _imp->viewerSubContainerLayout->addWidget(_imp->viewer); for (int i = 0; i < 2; ++i) { _imp->viewerSubContainerLayout->addWidget(_imp->infoWidget[i]); _imp->viewer->setInfoViewer(_imp->infoWidget[i], i); if (i == 1 || !infobarvisible) { _imp->infoWidget[i]->hide(); } } manageSlotsForInfoWidget(0, true); _imp->viewerLayout->addWidget(_imp->viewerSubContainer); _imp->mainLayout->addWidget(_imp->viewerContainer); TimeLinePtr timeline = app->getTimeLine(); _imp->timeLineGui = new TimeLineGui(node, timeline, getGui(), this); QObject::connect( _imp->timeLineGui, SIGNAL(boundariesChanged(SequenceTime,SequenceTime)), this, SLOT(onTimelineBoundariesChanged(SequenceTime,SequenceTime)) ); QObject::connect( app->getProject().get(), SIGNAL(frameRangeChanged(int,int)), _imp->timeLineGui, SLOT(onProjectFrameRangeChanged(int,int)) ); _imp->timeLineGui->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); if (!node->isTimelineVisible()) { _imp->timeLineGui->hide(); } //Add some spacing because the timeline might be black as the info _imp->mainLayout->addSpacing( TO_DPIY(5) ); _imp->mainLayout->addWidget(_imp->timeLineGui); double leftBound, rightBound; leftBound = node->getPlaybackInPointKnob()->getValue(); rightBound = node->getPlaybackOutPointKnob()->getValue(); TimeValue projectLeft, projectRight; app->getProject()->getFrameRange(&projectLeft, &projectRight); _imp->timeLineGui->setBoundaries(leftBound, rightBound); onTimelineBoundariesChanged(leftBound, rightBound); _imp->timeLineGui->setFrameRangeEdited(projectLeft != leftBound || projectRight != rightBound);; manageTimelineSlot(false, timeline); QObject::connect( node.get(), SIGNAL(renderStatsAvailable(int,double,RenderStatsMap)), this, SLOT(onRenderStatsAvailable(int,double,RenderStatsMap)) ); QObject::connect( _imp->viewer, SIGNAL(zoomChanged(int)), this, SLOT(updateZoomComboBox(int)) ); QObject::connect( node.get(), SIGNAL(viewerDisconnected()), this, SLOT(disconnectViewer()) ); createNodeViewerInterface(node_ui); setPluginViewerInterface(node_ui); for (std::list<NodeGuiPtr>::const_iterator it = existingNodesContext.begin(); it != existingNodesContext.end(); ++it) { ViewerNodePtr isViewerNode = (*it)->getNode()->isEffectViewerNode(); // For viewers, create the viewer interface separately if (!isViewerNode) { createNodeViewerInterface(*it); } } for (std::list<NodeGuiPtr>::const_iterator it = activePluginsContext.begin(); it != activePluginsContext.end(); ++it) { ViewerNodePtr isViewerNode = (*it)->getNode()->isEffectViewerNode(); // For viewers, create the viewer interface separately if (!isViewerNode) { setPluginViewerInterface(*it); } } setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); _imp->viewerNode.lock()->setUiContext( getViewer() ); QTimer::singleShot( 25, _imp->timeLineGui, SLOT(recenterOnBounds()) ); //Refresh the viewport lock state const std::list<ViewerTab*>& viewers = getGui()->getViewersList(); if ( !viewers.empty() ) { ViewerTab* other = viewers.front(); if ( other->getInternalNode()->isViewersSynchroEnabled() ) { double left, bottom, factor, par; other->getViewer()->getProjection(&left, &bottom, &factor, &par); _imp->viewer->setProjection(left, bottom, factor, par); node->setViewersSynchroEnabled(true); } } _imp->cachedFramesThread.reset(new CachedFramesThread(this)); _imp->cachedFramesThread->start(); }
TimeValue ViewerDisplayScheduler::getLastRenderedTime() const { ViewerNodePtr effect = getOutputNode()->isEffectViewerNode(); return TimeValue(effect->getLastRenderedTime()); }
TimeValue ViewerDisplayScheduler::timelineGetTime() const { ViewerNodePtr isViewer = getOutputNode()->isEffectViewerNode(); return TimeValue(isViewer->getTimeline()->currentFrame()); }
ViewerTab* Gui::addNewViewerTab(const NodeGuiPtr& node, TabWidget* where) { if (!node) { return 0; } ViewerNodePtr viewer = node->getNode()->isEffectViewerNode(); NodesGuiList activeNodeViewerUi, nodeViewerUi; //Don't create tracker & roto interface for file dialog preview viewer NodeCollectionPtr group = viewer->getNode()->getGroup(); if (group) { if ( !_imp->_viewerTabs.empty() ) { ( *_imp->_viewerTabs.begin() )->getNodesViewerInterface(&nodeViewerUi, &activeNodeViewerUi); } else { NodeGraph* graph = dynamic_cast<NodeGraph*>( group->getNodeGraph() ); if (!graph) { graph = _imp->_nodeGraphArea; } if (graph) { const NodesGuiList & allNodes = graph->getAllActiveNodes(); std::set<std::string> activeNodesPluginID; for (NodesGuiList::const_iterator it = allNodes.begin(); it != allNodes.end(); ++it) { nodeViewerUi.push_back( *it ); std::string pluginID = (*it)->getNode()->getPluginID(); std::set<std::string>::iterator found = activeNodesPluginID.find(pluginID); if ( found == activeNodesPluginID.end() ) { activeNodesPluginID.insert(pluginID); activeNodeViewerUi.push_back(*it); } } } } } std::string nodeName = node->getNode()->getFullyQualifiedName(); for (std::size_t i = 0; i < nodeName.size(); ++i) { if (nodeName[i] == '.') { nodeName[i] = '_'; } } std::string label; NodeGraph::makeFullyQualifiedLabel(node->getNode(), &label); ViewerTab* tab = new ViewerTab(nodeName, nodeViewerUi, activeNodeViewerUi, this, node, where); tab->setLabel(label); QObject::connect( tab->getViewer(), SIGNAL(imageChanged(int,bool)), this, SLOT(onViewerImageChanged(int,bool)) ); { QMutexLocker l(&_imp->_viewerTabsMutex); _imp->_viewerTabs.push_back(tab); if (!_imp->_activeViewer) { _imp->_activeViewer = tab; } } where->appendTab(tab, tab); Q_EMIT viewersChanged(); return tab; } // Gui::addNewViewerTab
static ActionRetCodeEnum createFrameRenderResultsForView(const ViewerNodePtr& viewer, const TreeRenderQueueProviderPtr& provider, const ViewerRenderFrameResultsContainerPtr& results, ViewIdx view, const RenderStatsPtr& stats, bool isPlayback, const RectD* partialUpdateRoIParam, unsigned int mipMapLevel, ViewerCompositingOperatorEnum viewerBlend, bool byPassCache, bool draftModeEnabled, bool fullFrameProcessing, bool viewerBEqualsViewerA, const RotoStrokeItemPtr& activeDrawingStroke) { // Initialize for each view a sub-result. // Each view has 2 renders: the A and B viewerprocess ViewerRenderFrameSubResultPtr subResult(new ViewerRenderFrameSubResult); results->frames.push_back(subResult); subResult->view = view; subResult->stats = stats; if (partialUpdateRoIParam) { subResult->textureTransferType = OpenGLViewerI::TextureTransferArgs::eTextureTransferTypeOverlay; } else if (activeDrawingStroke && activeDrawingStroke->getRenderCloneCurrentStrokeStartPointIndex() > 0) { // Upon painting ticks, we just have to update the viewer for the area that was painted subResult->textureTransferType = OpenGLViewerI::TextureTransferArgs::eTextureTransferTypeModify; } else { subResult->textureTransferType = OpenGLViewerI::TextureTransferArgs::eTextureTransferTypeReplace; } for (int viewerInputIndex = 0; viewerInputIndex < 2; ++viewerInputIndex) { subResult->perInputsData[viewerInputIndex].retCode = eActionStatusFailed; if (viewerInputIndex == 1 && (viewerBEqualsViewerA || viewerBlend == eViewerCompositingOperatorNone)) { if (viewerBEqualsViewerA && viewerBlend != eViewerCompositingOperatorNone) { subResult->copyInputBFromA = true; } continue; } if (viewer->isViewerPaused(viewerInputIndex)) { subResult->perInputsData[viewerInputIndex].retCode = eActionStatusAborted; continue; } ViewerInstancePtr viewerProcess = viewer->getViewerProcessNode(viewerInputIndex); subResult->perInputsData[viewerInputIndex].viewerProcessNode = viewerProcess->getNode(); TreeRender::CtorArgsPtr initArgs(new TreeRender::CtorArgs); initArgs->treeRootEffect = viewerProcess; initArgs->provider = provider; initArgs->time = results->time; initArgs->view = subResult->view; // Render by default on disk is always using a mipmap level of 0 but using the proxy scale of the project initArgs->mipMapLevel = mipMapLevel; #pragma message WARN("Todo: set proxy scale here") initArgs->proxyScale = RenderScale(1.); // Render the RoD if fullframe processing if (partialUpdateRoIParam) { initArgs->canonicalRoI = *partialUpdateRoIParam; } else { RectD roi; if (!fullFrameProcessing) { roi = viewerProcess->getViewerRoI(); } initArgs->canonicalRoI = roi; } initArgs->stats = stats; initArgs->activeRotoDrawableItem = activeDrawingStroke; initArgs->draftMode = draftModeEnabled; initArgs->playback = isPlayback; initArgs->byPassCache = byPassCache; initArgs->preventConcurrentTreeRenders = (activeDrawingStroke || partialUpdateRoIParam); if (!isPlayback && subResult->textureTransferType == OpenGLViewerI::TextureTransferArgs::eTextureTransferTypeReplace && !activeDrawingStroke) { subResult->perInputsData[viewerInputIndex].colorPickerNode = viewerInputIndex == 0 ? viewer->getCurrentAInput() : viewer->getCurrentBInput(); if (subResult->perInputsData[viewerInputIndex].colorPickerNode) { // Also sample the "main" input of the color picker node, this is useful for keyers. int mainInput = subResult->perInputsData[viewerInputIndex].colorPickerNode->getPreferredInput(); subResult->perInputsData[viewerInputIndex].colorPickerInputNode = subResult->perInputsData[viewerInputIndex].colorPickerNode->getInput(mainInput); } } if (subResult->perInputsData[viewerInputIndex].colorPickerNode) { initArgs->extraNodesToSample.push_back(subResult->perInputsData[viewerInputIndex].colorPickerNode); } if (subResult->perInputsData[viewerInputIndex].colorPickerInputImage) { initArgs->extraNodesToSample.push_back(subResult->perInputsData[viewerInputIndex].colorPickerInputNode); } subResult->perInputsData[viewerInputIndex].render = TreeRender::create(initArgs); if (!subResult->perInputsData[viewerInputIndex].render) { return eActionStatusFailed; } } // for each viewer input return eActionStatusOK; } // createFrameRenderResultsForView