tahoma2d/toonz/sources/toonz/sceneviewer.cpp
2020-08-07 00:11:19 -06:00

3332 lines
110 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifdef LINUX
#define GL_GLEXT_PROTOTYPES
#endif
// Toonz includes
#include "tapp.h"
#include "viewerpane.h"
#include "onionskinmaskgui.h"
#include "viewerdraw.h"
#include "menubarcommandids.h"
#include "ruler.h"
#include "locatorpopup.h"
#include "../stopmotion/stopmotion.h"
// TnzTools includes
#include "tools/cursors.h"
#include "tools/cursormanager.h"
#include "tools/toolhandle.h"
#include "tools/toolcommandids.h"
#include "tools/toolutils.h"
// TnzQt includes
#include "toonzqt/icongenerator.h"
#include "toonzqt/gutil.h"
#include "toonzqt/imageutils.h"
#include "toonzqt/lutcalibrator.h"
#include "toonzqt/viewcommandids.h"
// TnzLib includes
#include "toonz/tscenehandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/sceneproperties.h"
#include "toonz/toonzscene.h"
#include "toonz/levelset.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/tcamera.h"
#include "toonz/stage2.h"
#include "toonz/stage.h"
#include "toonz/stageplayer.h"
#include "toonz/stagevisitor.h"
#include "toonz/txsheet.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/tstageobjectspline.h"
#include "toonz/tobjecthandle.h"
#include "toonz/tonionskinmaskhandle.h"
#include "toonz/palettecontroller.h"
#include "toonz/tpalettehandle.h"
#include "toonz/childstack.h"
#include "toonz/dpiscale.h"
#include "toonz/txshlevel.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/preferences.h"
#include "toonz/glrasterpainter.h"
#include "toonz/cleanupparameters.h"
#include "toonz/toonzimageutils.h"
#include "toonz/txshleveltypes.h"
#include "subcameramanager.h"
// TnzCore includes
#include "tpalette.h"
#include "tropcm.h"
#include "tgl.h"
#include "tofflinegl.h"
#include "tstopwatch.h"
#include "trop.h"
#include "tproperty.h"
#include "timagecache.h"
#include "trasterimage.h"
#include "tstroke.h"
#include "ttoonzimage.h"
// Qt includes
#include <QMenu>
#include <QApplication>
#include <QDesktopWidget>
#if QT_VERSION >= 0x050000
#include <QInputMethod>
#else
#include <QInputContext>
#endif
#include <QGLContext>
#include <QOpenGLFramebufferObject>
#include <QMainWindow>
#include "sceneviewer.h"
void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d,
double pixelSize);
//-------------------------------------------------------------------------------
namespace {
int l_mainDisplayListsSpaceId =
-1; //!< Display lists space id associated with SceneViewers
std::set<TGlContext>
l_contexts; //!< Stores every SceneViewer context (see ~SceneViewer)
//-------------------------------------------------------------------------------
struct DummyProxy : public TGLDisplayListsProxy {
~DummyProxy() {}
void makeCurrent() {}
void doneCurrent() {}
};
//-------------------------------------------------------------------------------
double getActualFrameRate() {
// compute frame per second
static double fps = 0;
static TStopWatch stopwatch;
static int frame = 0;
++frame;
stopwatch.start();
unsigned long tt = stopwatch.getTotalTime();
// wait a time greater than one second
if (tt > 1000) {
stopwatch.stop();
fps = troundp(((1000 * frame) / (double)tt));
stopwatch.start(true);
frame = 0;
}
return fps;
}
//-----------------------------------------------------------------------------
void copyFrontBufferToBackBuffer() {
static GLint viewport[4];
static GLfloat raster_pos[4];
glGetIntegerv(GL_VIEWPORT, viewport);
/* set source buffer */
glReadBuffer(GL_FRONT);
/* set projection matrix */
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, viewport[2], 0, viewport[3]);
/* set modelview matrix */
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
/* save old raster position */
glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos);
/* set raster position */
glRasterPos4f(0.0, 0.0, 0.0, 1.0);
/* copy buffer */
glCopyPixels(0, 0, viewport[2], viewport[3], GL_COLOR);
/* restore old raster position */
glRasterPos4fv(raster_pos);
/* restore old matrices */
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
/* restore source buffer */
glReadBuffer(GL_BACK);
}
//-----------------------------------------------------------------------------
/*! Compute new 3Dposition and new 2D position. */
T3DPointD computeNew3DPosition(T3DPointD start3DPos, TPointD delta2D,
TPointD &new2dPos, GLdouble modelView3D[16],
GLdouble projection3D[16], GLint viewport3D[4],
int devPixRatio) {
GLdouble pos2D_x, pos2D_y, pos2D_z;
gluProject(-start3DPos.x, -start3DPos.y, start3DPos.z, modelView3D,
projection3D, viewport3D, &pos2D_x, &pos2D_y, &pos2D_z);
new2dPos = TPointD(pos2D_x + delta2D.x, pos2D_y + delta2D.y);
GLdouble pos3D_x, pos3D_y, pos3D_z;
gluUnProject(new2dPos.x, new2dPos.y, 1, modelView3D, projection3D, viewport3D,
&pos3D_x, &pos3D_y, &pos3D_z);
new2dPos.y = viewport3D[3] - new2dPos.y - 20 * devPixRatio;
return T3DPointD(pos3D_x, pos3D_y, pos3D_z);
}
//-----------------------------------------------------------------------------
#ifdef DA_RIVEDERE
// Il metodo copia una porzione del BACK_BUFFER definita da rect nel
// FRONTE_BUFFER
// Attualmente si notano delle brutture intorno al rettangolo.
// Per il momento si lavora nel BACK_BUFFER e sicopia tutto il FRONT_BUFFER.
// Riattivando questo metodo bisogna ricordarsi di disattivare lo swapbuffer in
// GLInvalidateRect prima di
// fare updateGL() e riattivarlo immediatamernte dopo!
void copyBackBufferToFrontBuffer(const TRect &rect) {
static GLint viewport[4];
static GLfloat raster_pos[4];
glGetIntegerv(GL_VIEWPORT, viewport);
/* set source buffer */
glReadBuffer(GL_BACK);
glDrawBuffer(GL_FRONT);
/* set projection matrix */
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, viewport[2], 0, viewport[3]);
/* set modelview matrix */
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
/* save old raster position */
glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos);
/* set raster position */
glRasterPos4f(0.0, 0.0, 0.0, 1.0);
/* copy buffer */
glCopyPixels(0, 0, viewport[2], viewport[3], GL_COLOR);
/* restore old raster position */
glRasterPos4fv(raster_pos);
/* restore old matrices */
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
/* restore source buffer */
glDrawBuffer(GL_BACK);
}
#endif
const TRectD InvalidateAllRect(0, 0, -1, -1);
//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------
//=============================================================================
// ToggleCommand
//-----------------------------------------------------------------------------
ToggleCommandHandler::ToggleCommandHandler(CommandId id, bool startStatus)
: MenuItemHandler(id), m_status(startStatus) {}
void ToggleCommandHandler::execute() {
m_status = !m_status;
// emit sceneChanged WITHOUT dirty flag
TApp::instance()->getCurrentScene()->notifySceneChanged(false);
}
//-----------------------------------------------------------------------------
ToggleCommandHandler viewTableToggle(MI_ViewTable, false);
ToggleCommandHandler editInPlaceToggle(MI_ToggleEditInPlace, false);
ToggleCommandHandler fieldGuideToggle(MI_FieldGuide, false);
ToggleCommandHandler safeAreaToggle(MI_SafeArea, false);
ToggleCommandHandler rasterizePliToggle(MI_RasterizePli, false);
ToggleCommandHandler viewClcToggle("MI_ViewColorcard", false);
ToggleCommandHandler viewCameraToggle("MI_ViewCamera", false);
ToggleCommandHandler viewBBoxToggle("MI_ViewBBox", false);
ToggleCommandHandler viewGuideToggle("MI_ViewGuide", false);
ToggleCommandHandler viewRulerToggle("MI_ViewRuler", false);
//-----------------------------------------------------------------------------
void invalidateIcons() {
ToonzCheck *tc = ToonzCheck::instance();
int mask = tc->getChecks();
IconGenerator::Settings s;
s.m_blackBgCheck = mask & ToonzCheck::eBlackBg;
s.m_transparencyCheck = mask & ToonzCheck::eTransparency;
s.m_inksOnly = mask & ToonzCheck::eInksOnly;
// emphasize lines with style#1 regardless of the current style
if (mask & ToonzCheck::eInk1) s.m_inkIndex = 1;
// emphasize lines with the current style
else if (mask & ToonzCheck::eInk)
s.m_inkIndex = tc->getColorIndex();
else
s.m_inkIndex = -1;
s.m_paintIndex = mask & ToonzCheck::ePaint ? tc->getColorIndex() : -1;
IconGenerator::instance()->setSettings(s);
// Force icons to refresh for Toonz Vector levels
TXshLevel *sl = TApp::instance()->getCurrentLevel()->getLevel();
if (sl && sl->getType() == PLI_XSHLEVEL) {
std::vector<TFrameId> fids;
sl->getFids(fids);
for (int i = 0; i < (int)fids.size(); i++)
IconGenerator::instance()->invalidate(sl, fids[i]);
}
// Do not remove icons here as they will be re-used for updating icons in the
// level strip
// emit sceneChanged WITHOUT dirty flag
TApp::instance()->getCurrentScene()->notifySceneChanged(false);
TApp::instance()->getCurrentLevel()->notifyLevelViewChange();
}
//--------------------------------------------------------------
static void executeCheck(int checkType) {
ToonzCheck::instance()->toggleCheck(checkType);
invalidateIcons();
}
//-----------------------------------------------------------------------------
class TCheckToggleCommand final : public MenuItemHandler {
public:
TCheckToggleCommand() : MenuItemHandler("MI_TCheck") {}
void execute() override { executeCheck(ToonzCheck::eTransparency); }
} tcheckToggle;
//-----------------------------------------------------------------------------
class ICheckToggleCommand final : public MenuItemHandler {
public:
ICheckToggleCommand() : MenuItemHandler("MI_ICheck") {}
void execute() override { executeCheck(ToonzCheck::eInk); }
} icheckToggle;
//-----------------------------------------------------------------------------
class PCheckToggleCommand final : public MenuItemHandler {
public:
PCheckToggleCommand() : MenuItemHandler("MI_PCheck") {}
void execute() override { executeCheck(ToonzCheck::ePaint); }
} pcheckToggle;
//-----------------------------------------------------------------------------
class BCheckToggleCommand final : public MenuItemHandler {
public:
BCheckToggleCommand() : MenuItemHandler("MI_BCheck") {}
void execute() override { executeCheck(ToonzCheck::eBlackBg); }
} bcheckToggle;
//-----------------------------------------------------------------------------
class TAutocloseToggleCommand final : public MenuItemHandler {
public:
TAutocloseToggleCommand() : MenuItemHandler("MI_ACheck") {}
void execute() override { executeCheck(ToonzCheck::eAutoclose); }
} tautocloseToggle;
//-----------------------------------------------------------------------------
class TGapToggleCommand final : public MenuItemHandler {
public:
TGapToggleCommand() : MenuItemHandler("MI_GCheck") {}
void execute() override { executeCheck(ToonzCheck::eGap); }
} tgapToggle;
//-----------------------------------------------------------------------------
class TInksOnlyToggleCommand final : public MenuItemHandler {
public:
TInksOnlyToggleCommand() : MenuItemHandler("MI_IOnly") {}
void execute() override { executeCheck(ToonzCheck::eInksOnly); }
} tinksOnlyToggle;
//-----------------------------------------------------------------------------
/*! emphasize lines with style#1 regardless of the current style
*/
class Ink1CheckToggleCommand final : public MenuItemHandler {
public:
Ink1CheckToggleCommand() : MenuItemHandler("MI_Ink1Check") {}
void execute() override { executeCheck(ToonzCheck::eInk1); }
} ink1checkToggle;
//=============================================================================
class TShiftTraceToggleCommand final : public MenuItemHandler {
CommandId m_cmdId;
public:
TShiftTraceToggleCommand(CommandId cmdId)
: MenuItemHandler(cmdId), m_cmdId(cmdId) {}
void execute() override {
CommandManager *cm = CommandManager::instance();
QAction *action = cm->getAction(m_cmdId);
bool checked = action->isChecked();
if (std::string(m_cmdId) == MI_ShiftTrace) {
cm->enable(MI_EditShift, checked);
cm->enable(MI_NoShift, checked);
if (checked) OnioniSkinMaskGUI::resetShiftTraceFrameOffset();
// cm->getAction(MI_NoShift)->setChecked(false);
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
} else if (std::string(m_cmdId) == MI_EditShift) {
if (checked) {
QAction *noShiftAction =
CommandManager::instance()->getAction(MI_NoShift);
if (noShiftAction) noShiftAction->setChecked(false);
TApp::instance()->getCurrentTool()->setPseudoTool("T_ShiftTrace");
} else {
TApp::instance()->getCurrentTool()->unsetPseudoTool();
}
CommandManager::instance()->enable(MI_NoShift, !checked);
} else if (std::string(m_cmdId) == MI_NoShift) {
}
updateShiftTraceStatus();
}
bool isChecked(CommandId id) const {
QAction *action = CommandManager::instance()->getAction(id);
return action != 0 && action->isChecked();
}
void updateShiftTraceStatus() {
OnionSkinMask::ShiftTraceStatus status = OnionSkinMask::DISABLED;
if (isChecked(MI_ShiftTrace)) {
if (isChecked(MI_EditShift))
status = OnionSkinMask::EDITING_GHOST;
else if (isChecked(MI_NoShift))
status = OnionSkinMask::ENABLED_WITHOUT_GHOST_MOVEMENTS;
else
status = OnionSkinMask::ENABLED;
}
OnionSkinMask osm =
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
osm.setShiftTraceStatus(status);
osm.clearGhostFlipKey();
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
}
};
TShiftTraceToggleCommand shiftTraceToggleCommand(MI_ShiftTrace),
editShiftToggleCommand(MI_EditShift), noShiftToggleCommand(MI_NoShift);
class TResetShiftTraceCommand final : public MenuItemHandler {
public:
TResetShiftTraceCommand() : MenuItemHandler(MI_ResetShift) {}
void execute() override {
OnionSkinMask osm =
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
osm.setShiftTraceGhostCenter(0, TPointD());
osm.setShiftTraceGhostCenter(1, TPointD());
osm.setShiftTraceGhostAff(0, TAffine());
osm.setShiftTraceGhostAff(1, TAffine());
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool) tool->reset();
}
} resetShiftTraceCommand;
//-----------------------------------------------------------------------------
// Following commands (VB_***) are registered for command bar buttons.
// They are separatd from the original visalization commands
// so that they will not break a logic of ShortcutZoomer.
class TViewResetCommand final : public MenuItemHandler {
public:
TViewResetCommand() : MenuItemHandler(VB_ViewReset) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->resetSceneViewer();
}
} viewResetCommand;
class TZoomResetCommand final : public MenuItemHandler {
public:
TZoomResetCommand() : MenuItemHandler(VB_ZoomReset) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->resetZoom();
}
} zoomResetCommand;
class TZoomFitCommand final : public MenuItemHandler {
public:
TZoomFitCommand() : MenuItemHandler(VB_ZoomFit) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->fitToCamera();
}
} zoomFitCommand;
class TActualPixelSizeCommand final : public MenuItemHandler {
public:
TActualPixelSizeCommand() : MenuItemHandler(VB_ActualPixelSize) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->setActualPixelSize();
}
} actualPixelSizeCommand;
class TFlipViewerXCommand final : public MenuItemHandler {
public:
TFlipViewerXCommand() : MenuItemHandler(VB_FlipX) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->flipX();
}
} flipViewerXCommand;
class TFlipViewerYCommand final : public MenuItemHandler {
public:
TFlipViewerYCommand() : MenuItemHandler(VB_FlipY) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->flipY();
}
} flipViewerYCommand;
class TRotateResetCommand final : public MenuItemHandler {
public:
TRotateResetCommand() : MenuItemHandler(VB_RotateReset) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->resetRotation();
}
} rotateResetCommand;
class TPositionResetCommand final : public MenuItemHandler {
public:
TPositionResetCommand() : MenuItemHandler(VB_PositionReset) {}
void execute() override {
if (TApp::instance()->getActiveViewer())
TApp::instance()->getActiveViewer()->resetPosition();
}
} positionResetCommand;
class TVectorGuidedDrawingToggleCommand final : public MenuItemHandler {
public:
TVectorGuidedDrawingToggleCommand()
: MenuItemHandler(MI_VectorGuidedDrawing) {}
void execute() override {
CommandManager *cm = CommandManager::instance();
QAction *action = cm->getAction(MI_VectorGuidedDrawing);
Preferences::instance()->setValue(guidedDrawingType,
action->isChecked() ? 1 : 0);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp::instance()->getCurrentScene()->notifyPreferenceChanged(
"GuidedDrawingFrame");
}
bool isChecked(CommandId id) const {
QAction *action = CommandManager::instance()->getAction(id);
return action != 0 && action->isChecked();
}
} vectorGuidedDrawingToggleCommand;
class TSelectGuideStrokeResetCommand final : public MenuItemHandler {
public:
TSelectGuideStrokeResetCommand()
: MenuItemHandler(MI_SelectGuideStrokeReset) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->getViewer()->setGuidedStrokePickerMode(0);
tool->getViewer()->setGuidedBackStroke(-1);
tool->getViewer()->setGuidedFrontStroke(-1);
tool->getViewer()->invalidateAll();
}
} SelectGuideStrokeResetCommand;
class TSelectGuideStrokeNextCommand final : public MenuItemHandler {
public:
TSelectGuideStrokeNextCommand() : MenuItemHandler(MI_SelectNextGuideStroke) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->getViewer()->setGuidedStrokePickerMode(1);
}
} selectGuideStrokeNextCommand;
class TSelectGuideStrokePrevCommand final : public MenuItemHandler {
public:
TSelectGuideStrokePrevCommand() : MenuItemHandler(MI_SelectPrevGuideStroke) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->getViewer()->setGuidedStrokePickerMode(-1);
}
} selectGuideStrokePrevCommand;
class TSelectBothGuideStrokesCommand final : public MenuItemHandler {
public:
TSelectBothGuideStrokesCommand()
: MenuItemHandler(MI_SelectBothGuideStrokes) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->getViewer()->setGuidedStrokePickerMode(-2);
}
} selectBothGuideStrokesCommand;
class TTweenGuideStrokesCommand final : public MenuItemHandler {
public:
TTweenGuideStrokesCommand() : MenuItemHandler(MI_TweenGuideStrokes) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->tweenSelectedGuideStrokes();
}
} tweenGuideStrokesCommand;
class TTweenGuideStrokeToSelectedCommand final : public MenuItemHandler {
public:
TTweenGuideStrokeToSelectedCommand()
: MenuItemHandler(MI_TweenGuideStrokeToSelected) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->tweenGuideStrokeToSelected();
}
} tweenGuideStrokeToSelectedCommand;
class TSelectGuidesAndTweenCommand final : public MenuItemHandler {
public:
TSelectGuidesAndTweenCommand()
: MenuItemHandler(MI_SelectGuidesAndTweenMode) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->getViewer()->setGuidedStrokePickerMode(-3);
}
} selectGuidesAndTweenCommand;
class TFlipNextStrokeDirectionCommand final : public MenuItemHandler {
public:
TFlipNextStrokeDirectionCommand() : MenuItemHandler(MI_FlipNextGuideStroke) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->flipGuideStrokeDirection(1);
}
} flipNextStrokeDirectionCommand;
class TFlipPrevStrokeDirectionCommand final : public MenuItemHandler {
public:
TFlipPrevStrokeDirectionCommand() : MenuItemHandler(MI_FlipPrevGuideStroke) {}
void execute() override {
TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
if (!vi) return;
Preferences *pref = Preferences::instance();
if (!pref->isOnionSkinEnabled() || (pref->getGuidedDrawingType() != 1 &&
pref->getGuidedDrawingType() != 2))
return;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (!tool) return;
tool->flipGuideStrokeDirection(-1);
}
} flipPrevStrokeDirectionCommand;
//=============================================================================
// SceneViewer
//-----------------------------------------------------------------------------
SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent)
: GLWidgetForHighDpi(parent)
, m_pressure(0)
, m_lastMousePos(0, 0)
, m_mouseButton(Qt::NoButton)
, m_foregroundDrawing(false)
, m_tabletEvent(false)
, m_tabletMove(false)
, m_buttonClicked(false)
, m_referenceMode(NORMAL_REFERENCE)
, m_previewMode(NO_PREVIEW)
, m_isMouseEntered(false)
, m_forceGlFlush(true)
, m_freezedStatus(NO_FREEZED)
, m_viewGrabImage(0)
, m_FPS(0)
, m_hRuler(0)
, m_vRuler(0)
, m_viewMode(SCENE_VIEWMODE)
, m_pos(0, 0)
, m_pan3D(TPointD(0, 0))
, m_zoomScale3D(0.1)
, m_theta3D(20)
, m_phi3D(30)
, m_dpiScale(TPointD(1, 1))
, m_compareSettings()
, m_minZ(0)
, m_tableDLId(-1)
, m_groupIndexToBeEntered(-1)
, m_pixelSize(1)
, m_eraserPointerOn(false)
, m_backupTool("")
, m_clipRect()
, m_isPicking(false)
, m_current3DDevice(NONE)
, m_sideRasterPos()
, m_topRasterPos()
, m_toolDisableReason("")
, m_editPreviewSubCamera(false)
, m_locator(NULL)
, m_isLocator(false)
, m_isBusyOnTabletMove(false) {
m_visualSettings.m_sceneProperties =
TApp::instance()->getCurrentScene()->getScene()->getProperties();
m_stopMotion = StopMotion::instance();
// Enables multiple key input.
setAttribute(Qt::WA_KeyCompression);
// Enables input methods for Asian languages.
setAttribute(Qt::WA_InputMethodEnabled);
setFocusPolicy(Qt::StrongFocus);
setAcceptDrops(true);
this->setMouseTracking(true);
// introduced from Qt 5.9
#if QT_VERSION >= 0x050900
this->setTabletTracking(true);
#endif
for (int i = 0; i < m_viewAff.size(); ++i) {
setViewMatrix(getNormalZoomScale(), i);
m_rotationAngle[i] = 0.0;
}
m_3DSideR = rasterFromQPixmap(svgToPixmap(":Resources/3Dside_r.svg"));
m_3DSideL = rasterFromQPixmap(svgToPixmap(":Resources/3Dside_l.svg"));
m_3DTop = rasterFromQPixmap(svgToPixmap(":Resources/3Dtop.svg"));
setAttribute(Qt::WA_AcceptTouchEvents);
grabGesture(Qt::SwipeGesture);
grabGesture(Qt::PanGesture);
grabGesture(Qt::PinchGesture);
setUpdateBehavior(QOpenGLWidget::PartialUpdate);
if (Preferences::instance()->isColorCalibrationEnabled())
m_lutCalibrator = new LutCalibrator();
}
//-----------------------------------------------------------------------------
void SceneViewer::setVisual(const ImagePainter::VisualSettings &settings) {
// m_visualSettings.m_blankColor = settings.m_blankColor;//for the blank
// frames, I don't have to repaint the viewer are using updateGl!
bool repaint = m_visualSettings.needRepaint(settings);
m_visualSettings = settings;
m_visualSettings.m_sceneProperties =
TApp::instance()->getCurrentScene()->getScene()->getProperties();
if (repaint) GLInvalidateAll();
}
//-----------------------------------------------------------------------------
SceneViewer::~SceneViewer() {
if (m_fbo) delete m_fbo;
// release all the registered context (once when exit the software)
std::set<TGlContext>::iterator ct, cEnd(l_contexts.end());
for (ct = l_contexts.begin(); ct != cEnd; ++ct)
TGLDisplayListsManager::instance()->releaseContext(*ct);
l_contexts.clear();
}
//-------------------------------------------------------------------------------
// Builds the view area, in camera reference
TRectD SceneViewer::getPreviewRect() const {
TApp *app = TApp::instance();
int row = app->getCurrentFrame()->getFrame();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
double cameraZ = xsh->getZ(cameraId, row);
TAffine cameraAff =
xsh->getPlacement(cameraId, row) * TScale((1000 + cameraZ) / 1000);
TDimensionD cameraSize =
app->getCurrentScene()->getScene()->getCurrentCamera()->getSize();
TDimension cameraRes =
app->getCurrentScene()->getScene()->getCurrentCamera()->getRes();
TScale cameraScale(Stage::inch * cameraSize.lx / cameraRes.lx,
Stage::inch * cameraSize.ly / cameraRes.ly);
TRectD viewRect(-width() * 0.5, -height() * 0.5, width() * 0.5,
height() * 0.5);
return (getViewMatrix() * cameraAff * cameraScale).inv() * viewRect;
}
//-------------------------------------------------------------------------------
void SceneViewer::onRenderStarted(int frame) { emit previewStatusChanged(); }
//-------------------------------------------------------------------------------
void SceneViewer::onRenderCompleted(int frame) {
invalidateAll();
emit previewStatusChanged();
}
//-------------------------------------------------------------------------------
void SceneViewer::onPreviewUpdate() {
update();
emit previewStatusChanged();
}
//-----------------------------------------------------------------------------
void SceneViewer::setReferenceMode(int referenceMode) {
if (m_referenceMode == referenceMode) return;
TApp *app = TApp::instance();
if (app->getCurrentFrame()->isEditingLevel())
app->getCurrentFrame()->setFrame(app->getCurrentFrame()->getFrame());
if (m_freezedStatus != NO_FREEZED) {
freeze(false);
emit freezeStateChanged(false);
}
m_referenceMode = referenceMode;
invalidateAll();
emit onZoomChanged();
}
//-------------------------------------------------------------------------------
void SceneViewer::freeze(bool on) {
if (!on) {
m_viewGrabImage = TRaster32P();
m_freezedStatus = NO_FREEZED;
} else {
setCursor(Qt::ForbiddenCursor);
m_freezedStatus = UPDATE_FREEZED;
}
GLInvalidateAll();
}
//-------------------------------------------------------------------------------
void SceneViewer::enablePreview(int previewMode) {
if (m_previewMode == previewMode) return;
TApp *app = TApp::instance();
if (app->getCurrentFrame()->isEditingLevel() && previewMode != NO_PREVIEW)
app->getCurrentFrame()->setFrame(app->getCurrentFrame()->getFrame());
if (m_freezedStatus != NO_FREEZED) {
freeze(false);
emit freezeStateChanged(false);
}
if (m_previewMode != NO_PREVIEW)
Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)
->removeListener(this);
// Schedule as a listener to Previewer.
if (previewMode != NO_PREVIEW) {
Previewer *previewer =
Previewer::instance(previewMode == SUBCAMERA_PREVIEW);
previewer->addListener(this);
previewer->update();
}
m_previewMode = previewMode;
GLInvalidateAll();
// for updating the title bar
emit previewToggled();
}
//-----------------------------------------------------------------------------
TPointD SceneViewer::winToWorld(const QPointF &pos) const {
// coordinate window (origine in alto a sinistra) -> coordinate colonna
// (origine al centro dell'immagine)
TPointD pp(pos.x() - (double)width() / 2.0,
-pos.y() + (double)height() / 2.0);
if (is3DView()) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
double z = xsh->getStageObject(cameraId)->getZ(
TApp::instance()->getCurrentFrame()->getFrame());
TPointD p(pp.x - m_pan3D.x, pp.y - m_pan3D.y);
p = p * (1 / m_zoomScale3D);
double theta = m_theta3D * M_PI_180;
double phi = m_phi3D * M_PI_180;
double cs_phi = cos(phi);
double sn_phi = sin(phi);
double cs_theta = cos(theta);
double sn_theta = sin(theta);
TPointD a(cs_phi, sn_theta * sn_phi); // proiezione di (1,0,0)
TPointD b(0, cs_theta); // proiezione di (0,1,0)
TPointD c(sn_phi, -sn_theta * cs_phi); // proiezione di (0,0,1)
TPointD aa = rotate90(a);
TPointD bb = rotate90(b);
double abb = a * bb;
double baa = b * aa;
if (fabs(abb) > 0.001 && fabs(baa) > 0.001) {
p -= c * z;
TPointD g((p * bb) / (a * bb), (p * aa) / (b * aa));
return TAffine() * g;
} else
return TAffine() * TPointD(0, 0);
}
return getViewMatrix().inv() * pp;
}
//-----------------------------------------------------------------------------
TPointD SceneViewer::winToWorld(const TPointD &winPos) const {
return winToWorld(QPointF(winPos.x, height() - winPos.y));
}
//-----------------------------------------------------------------------------
TPointD SceneViewer::worldToPos(const TPointD &worldPos) const {
TPointD p = getViewMatrix() * worldPos;
return TPointD(width() / 2 + p.x, height() / 2 + p.y);
}
//-----------------------------------------------------------------------------
void SceneViewer::showEvent(QShowEvent *) {
m_visualSettings.m_sceneProperties =
TApp::instance()->getCurrentScene()->getScene()->getProperties();
// Se il viewer e' show e il preview e' attivo aggiungo il listner al preview
if (m_previewMode != NO_PREVIEW)
Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)->addListener(this);
TApp *app = TApp::instance();
TSceneHandle *sceneHandle = app->getCurrentScene();
connect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(resetSceneViewer()));
connect(sceneHandle, SIGNAL(sceneChanged()), this, SLOT(onSceneChanged()));
TFrameHandle *frameHandle = app->getCurrentFrame();
connect(frameHandle, SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
TPaletteHandle *paletteHandle =
app->getPaletteController()->getCurrentLevelPalette();
connect(paletteHandle, SIGNAL(colorStyleChanged(bool)), this, SLOT(update()));
connect(app->getCurrentObject(), SIGNAL(objectSwitched()), this,
SLOT(onObjectSwitched()));
connect(app->getCurrentObject(), SIGNAL(objectChanged(bool)), this,
SLOT(update()));
connect(app->getCurrentOnionSkin(), SIGNAL(onionSkinMaskChanged()), this,
SLOT(onOnionSkinMaskChanged()));
connect(app->getCurrentLevel(), SIGNAL(xshLevelChanged()), this,
SLOT(update()));
connect(app->getCurrentLevel(), SIGNAL(xshCanvasSizeChanged()), this,
SLOT(update()));
// when level is switched, update m_dpiScale in order to show white background
// for Ink&Paint work properly
connect(app->getCurrentLevel(), SIGNAL(xshLevelSwitched(TXshLevel *)), this,
SLOT(onLevelSwitched()));
connect(app->getCurrentXsheet(), SIGNAL(xsheetChanged()), this,
SLOT(onXsheetChanged()));
connect(app->getCurrentXsheet(), SIGNAL(xsheetSwitched()), this,
SLOT(update()));
// update tooltip when tool options are changed
connect(app->getCurrentTool(), SIGNAL(toolChanged()), this,
SLOT(onToolChanged()));
connect(app->getCurrentTool(), SIGNAL(toolCursorTypeChanged()), this,
SLOT(onToolChanged()));
connect(app, SIGNAL(tabletLeft()), this, SLOT(resetTabletStatus()));
if (m_stopMotion) {
connect(m_stopMotion, SIGNAL(newLiveViewImageReady()), this,
SLOT(onNewStopMotionImageReady()));
connect(m_stopMotion, SIGNAL(liveViewStopped()), this,
SLOT(onStopMotionLiveViewStopped()));
}
if (m_hRuler && m_vRuler) {
if (!viewRulerToggle.getStatus()) {
m_hRuler->hide();
m_vRuler->hide();
} else {
m_hRuler->show();
m_vRuler->show();
}
}
if (m_shownOnce == false) {
fitToCamera();
m_shownOnce = true;
}
TApp::instance()->setActiveViewer(this);
update();
}
//-----------------------------------------------------------------------------
void SceneViewer::hideEvent(QHideEvent *) {
// Se il viewer e' hide e il preview e' attivo rimuovo il listner dal preview
if (m_previewMode != NO_PREVIEW)
Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)
->removeListener(this);
TApp *app = TApp::instance();
TSceneHandle *sceneHandle = app->getCurrentScene();
if (sceneHandle) sceneHandle->disconnect(this);
TFrameHandle *frameHandle = app->getCurrentFrame();
if (frameHandle) frameHandle->disconnect(this);
TPaletteHandle *paletteHandle =
app->getPaletteController()->getCurrentLevelPalette();
if (paletteHandle) paletteHandle->disconnect(this);
TObjectHandle *objectHandle = app->getCurrentObject();
if (objectHandle) objectHandle->disconnect(this);
TOnionSkinMaskHandle *onionHandle = app->getCurrentOnionSkin();
if (onionHandle) onionHandle->disconnect(this);
TXshLevelHandle *levelHandle = app->getCurrentLevel();
if (levelHandle) levelHandle->disconnect(this);
TXsheetHandle *xsheetHandle = app->getCurrentXsheet();
if (xsheetHandle) xsheetHandle->disconnect(this);
ToolHandle *toolHandle = app->getCurrentTool();
if (toolHandle) toolHandle->disconnect(this);
disconnect(app, SIGNAL(tabletLeft()), this, SLOT(resetTabletStatus()));
if (m_stopMotion) {
disconnect(m_stopMotion, SIGNAL(newImageReady()), this,
SLOT(onNewStopMotionImageReady()));
disconnect(m_stopMotion, SIGNAL(liveViewStopped()), this,
SLOT(onStopMotionLiveViewStopped()));
}
// hide locator
if (m_locator && m_locator->isVisible()) m_locator->hide();
}
int SceneViewer::getVGuideCount() {
if (viewGuideToggle.getStatus())
return m_vRuler->getGuideCount();
else
return 0;
}
int SceneViewer::getHGuideCount() {
if (viewGuideToggle.getStatus())
return m_hRuler->getGuideCount();
else
return 0;
}
double SceneViewer::getVGuide(int index) { return m_vRuler->getGuide(index); }
double SceneViewer::getHGuide(int index) { return m_hRuler->getGuide(index); }
//-----------------------------------------------------------------------------
void SceneViewer::onNewStopMotionImageReady() {
if (m_stopMotion->m_hasLineUpImage) {
// if (m_hasStopMotionLineUpImage) delete m_stopMotionLineUpImage;
m_stopMotionLineUpImage =
(TRasterImageP)m_stopMotion->m_lineUpImage->clone();
m_stopMotionLineUpImage->setDpi(m_stopMotion->m_liveViewDpi.x,
m_stopMotion->m_liveViewDpi.y);
m_hasStopMotionLineUpImage = true;
}
if (m_stopMotion->m_hasLiveViewImage) {
// if (m_hasStopMotionImage) delete m_stopMotionImage;
m_stopMotionImage = m_stopMotion->m_liveViewImage->clone();
m_stopMotionImage->setDpi(m_stopMotion->m_liveViewDpi.x,
m_stopMotion->m_liveViewDpi.y);
m_hasStopMotionImage = true;
if (m_stopMotion->m_canon->m_pickLiveViewZoom) {
setToolCursor(this, ToolCursor::ZoomCursor);
}
onSceneChanged();
}
}
//-----------------------------------------------------------------------------
void SceneViewer::onStopMotionLiveViewStopped() {
m_hasStopMotionImage = false;
m_hasStopMotionLineUpImage = false;
onSceneChanged();
}
//-----------------------------------------------------------------------------
void SceneViewer::initializeGL() {
initializeOpenGLFunctions();
registerContext();
// to be computed once through the software
if (m_lutCalibrator && !m_lutCalibrator->isInitialized()) {
m_lutCalibrator->initialize();
connect(context(), SIGNAL(aboutToBeDestroyed()), this,
SLOT(onContextAboutToBeDestroyed()));
}
// glClearColor(1.0,1.0,1.0,1);
glClear(GL_COLOR_BUFFER_BIT);
if (m_firstInitialized)
m_firstInitialized = false;
else {
resizeGL(width(), height());
update();
}
}
//-----------------------------------------------------------------------------
void SceneViewer::resizeGL(int w, int h) {
w *= getDevPixRatio();
h *= getDevPixRatio();
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, -4000, 4000);
m_projectionMatrix.setToIdentity();
m_projectionMatrix.ortho(0, w, 0, h, -4000, 4000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375, 0.375, 0.0);
// make the center of the viewer = origin
glTranslated(w * 0.5, h * 0.5, 0);
if (m_freezedStatus == NORMAL_FREEZED) m_freezedStatus = UPDATE_FREEZED;
if (m_previewMode != NO_PREVIEW) requestTimedRefresh();
// remake fbo with new size
if (m_lutCalibrator && m_lutCalibrator->isValid()) {
if (m_fbo) delete m_fbo;
m_fbo = new QOpenGLFramebufferObject(w, h);
}
// for updating the navigator in levelstrip
emit refreshNavi();
}
//-----------------------------------------------------------------------------
void SceneViewer::drawBuildVars() {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
// Camera affine
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
TStageObject *camera = xsh->getStageObject(cameraId);
TAffine cameraPlacement = camera->getPlacement(frame);
double cameraZ = camera->getZ(frame);
m_drawCameraAff =
getViewMatrix() * cameraPlacement * TScale((1000 + cameraZ) / 1000);
// Table affine
TStageObject *table = xsh->getStageObject(TStageObjectId::TableId);
TAffine tablePlacement = table->getPlacement(frame);
double tableZ = table->getZ(frame);
TAffine placement;
m_drawIsTableVisible = TStageObject::perspective(
placement, cameraPlacement, cameraZ, tablePlacement, tableZ, 0);
m_drawTableAff = getViewMatrix() * tablePlacement;
// Camera test check
m_drawCameraTest = CameraTestCheck::instance()->isEnabled();
if (m_previewMode == NO_PREVIEW) {
m_drawEditingLevel = app->getCurrentFrame()->isEditingLevel();
m_viewMode = m_drawEditingLevel ? LEVEL_VIEWMODE : SCENE_VIEWMODE;
m_draw3DMode = is3DView() && (m_previewMode != SUBCAMERA_PREVIEW);
} else {
m_drawEditingLevel = false;
m_viewMode = app->getCurrentFrame()->isEditingLevel();
m_draw3DMode = false;
}
// Clip rect
if (!m_clipRect.isEmpty() && !m_draw3DMode) {
m_actualClipRect = getActualClipRect(getViewMatrix());
m_actualClipRect += TPoint(width() * 0.5, height() * 0.5);
}
TTool *tool = app->getCurrentTool()->getTool();
if (tool && !m_isLocator) tool->setViewer(this);
}
//-----------------------------------------------------------------------------
void SceneViewer::drawEnableScissor() {
if (!m_clipRect.isEmpty() && !m_draw3DMode) {
glEnable(GL_SCISSOR_TEST);
glScissor(m_actualClipRect.x0, m_actualClipRect.y0,
m_actualClipRect.getLx(), m_actualClipRect.getLy());
}
}
//-----------------------------------------------------------------------------
void SceneViewer::drawDisableScissor() {
if (!m_clipRect.isEmpty() && !m_draw3DMode) {
glDisable(GL_SCISSOR_TEST);
}
// clear the clipping rect
m_clipRect.empty();
}
//-----------------------------------------------------------------------------
void SceneViewer::drawBackground() {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
if (m_visualSettings.m_colorMask == 0) {
TPixel32 bgColor;
if (isPreviewEnabled()) {
if (Preferences::instance()->getUseThemeViewerColors()) {
QColor qtBgColor = getPreviewBGColor();
bgColor =
TPixel32(qtBgColor.red(), qtBgColor.green(), qtBgColor.blue());
} else
bgColor = Preferences::instance()->getPreviewBgColor();
} else {
if (Preferences::instance()->getUseThemeViewerColors()) {
QColor qtBgColor = getBGColor();
bgColor =
TPixel32(qtBgColor.red(), qtBgColor.green(), qtBgColor.blue());
} else
bgColor = Preferences::instance()->getViewerBgColor();
}
glClearColor(bgColor.r / 255.0f, bgColor.g / 255.0f, bgColor.b / 255.0f,
1.0);
} else
glClearColor(0, 0, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
if (glGetError() == GL_INVALID_FRAMEBUFFER_OPERATION) {
/* 起動時一回目になぜか GL_FRAMEBUFFER_COMPLETE なのに invalid operation
* が出る */
GLenum status = 0;
#ifdef _WIN32
PROC proc = wglGetProcAddress("glCheckFramebufferStatusEXT");
if (proc != nullptr)
status = reinterpret_cast<PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC>(proc)(
GL_FRAMEBUFFER);
#else
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
#endif
printf("GL_INVALID_FRAMEBUFFER_OPERATION: framebuffer:%d\n", status);
}
}
static bool check_framebuffer_status() {
#ifdef _WIN32
PROC proc = wglGetProcAddress("glCheckFramebufferStatusEXT");
if (proc == nullptr) return true;
GLenum s = reinterpret_cast<PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC>(proc)(
GL_FRAMEBUFFER);
#else
GLenum s = glCheckFramebufferStatus(GL_FRAMEBUFFER);
#endif
if (s == GL_FRAMEBUFFER_UNDEFINED)
printf("Warning: FB undefined: %d\n", s);
else if (s == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
printf("Warning: FB incomplete(attachment): %d\n", s);
else if (s == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
printf("Warning: FB incomplete(missing attachment): %d\n", s);
else if (s == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)
printf("Warning: FB incomplete(draw buffer): %d\n", s);
else if (s == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
printf("Warning: FB incomplete(read buffer): %d\n", s);
else if (s == GL_FRAMEBUFFER_UNSUPPORTED)
printf("Warning: FB unsupported: %d\n", s);
else if (s == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)
printf("Warning: FB incomplete(multi-sample): %d\n", s);
else if (s == GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS)
printf("Warning: FB incomplete(multi-sample): %d\n", s);
else if (s == GL_FRAMEBUFFER_COMPLETE)
;
else
printf("Warning: FB not complete(unknown cause): %d\n", s);
return s == GL_FRAMEBUFFER_COMPLETE;
}
//-----------------------------------------------------------------------------
void SceneViewer::drawCameraStand() {
GLint e = glGetError();
check_framebuffer_status();
assert(e == GL_NO_ERROR);
TXshSimpleLevel::m_rasterizePli = rasterizePliToggle.getStatus();
// clear
if (m_draw3DMode) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glClear(GL_DEPTH_BUFFER_BIT);
glPushMatrix();
mult3DMatrix();
} else
glDisable(GL_DEPTH_TEST);
assert(glGetError() == GL_NO_ERROR);
// draw table
if (!m_draw3DMode && viewTableToggle.getStatus() && m_drawIsTableVisible &&
m_visualSettings.m_colorMask == 0 && m_drawEditingLevel == false &&
!m_drawCameraTest) {
glPushMatrix();
tglMultMatrix(m_drawTableAff);
ViewerDraw::drawDisk(m_tableDLId);
glPopMatrix();
}
// draw colorcard (with camera BG color)
// Hide camera BG when level editing mode.
if (m_drawEditingLevel == false && viewClcToggle.getStatus() &&
!m_drawCameraTest) {
glPushMatrix();
tglMultMatrix(m_drawCameraAff);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
ViewerDraw::drawColorcard(m_visualSettings.m_colorMask);
glDisable(GL_BLEND);
glPopMatrix();
}
// Show white background when level editing mode.
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (m_drawEditingLevel && tool && tool->isEnabled()) {
if (!m_isLocator) tool->setViewer(this);
glPushMatrix();
if (m_referenceMode == CAMERA3D_REFERENCE) {
mult3DMatrix();
tglMultMatrix(tool->getMatrix());
} else {
tglMultMatrix(getViewMatrix() * tool->getMatrix());
}
glScaled(m_dpiScale.x, m_dpiScale.y, 1);
TImageP image = tool->getImage(false);
TToonzImageP ti = image;
TRasterImageP ri = image;
if (ti) {
TRect imgRect(0, 0, ti->getSize().lx - 1, ti->getSize().ly - 1);
TRectD bbox = ToonzImageUtils::convertRasterToWorld(imgRect, ti);
TPixel32 imgRectColor;
// draw black rectangle instead, if the BlackBG check is ON
if (ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg)
imgRectColor = TPixel::Black;
else
imgRectColor = Preferences::instance()->getLevelEditorBoxColor();
ToolUtils::fillRect(bbox * ti->getSubsampling(), imgRectColor);
} else if (ri) {
TRectD bbox = ri->getBBox();
bbox.x0 -= ri->getBBox().getLx() * 0.5;
bbox.x1 -= ri->getBBox().getLx() * 0.5;
bbox.y0 -= ri->getBBox().getLy() * 0.5;
bbox.y1 -= ri->getBBox().getLy() * 0.5;
TPixel32 imgRectColor;
// draw black rectangle instead, if the BlackBG check is ON
if (ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg)
imgRectColor = TPixel::Black;
else
imgRectColor = Preferences::instance()->getLevelEditorBoxColor();
ToolUtils::fillRect(bbox * ri->getSubsampling(), imgRectColor);
}
glPopMatrix();
}
// draw 3dframe
if (m_draw3DMode) {
glDepthFunc(GL_LEQUAL);
ViewerDraw::draw3DFrame(m_minZ, m_phi3D);
glDepthFunc(GL_ALWAYS);
}
// draw scene
assert(glGetError() == GL_NO_ERROR);
drawScene();
assert((glGetError()) == GL_NO_ERROR);
}
//-----------------------------------------------------------------------------
void SceneViewer::drawPreview() {
const double inch = Stage::inch;
TApp *app = TApp::instance();
int row = app->getCurrentFrame()->getFrame();
TCamera *currCamera = app->getCurrentScene()->getScene()->getCurrentCamera();
TDimensionD cameraSize = currCamera->getSize();
Previewer *previewer =
Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW);
TRasterP ras =
previewer->getRaster(row, m_visualSettings.m_recomputeIfNeeded);
if (ras) {
TRectD previewStageRectD, cameraStageRectD = currCamera->getStageRect();
TRect subCameraRect(currCamera->getInterestRect());
if (m_previewMode == SUBCAMERA_PREVIEW && subCameraRect.getLx() > 0 &&
subCameraRect.getLy() > 0)
previewStageRectD = currCamera->getInterestStageRect();
else
previewStageRectD = cameraStageRectD;
TAffine rasterToStageRef(
previewStageRectD.getLx() / ras->getLx(), 0.0,
previewStageRectD.x0 + 0.5 * previewStageRectD.getLx(), 0.0,
previewStageRectD.getLy() / ras->getLy(),
previewStageRectD.y0 + 0.5 * previewStageRectD.getLy());
TDimension dim(width(), height());
TAffine finalAff = m_drawCameraAff * rasterToStageRef;
m_visualSettings.m_useTexture = !Preferences::instance()->useDrawPixel();
ImagePainter::paintImage(TRasterImageP(ras), ras->getSize(), dim, finalAff,
m_visualSettings, m_compareSettings, TRect());
}
glPushMatrix();
tglMultMatrix(m_drawCameraAff);
TRectD frameRect(cameraSize);
frameRect.x1 *= inch;
frameRect.y1 *= inch;
frameRect -= 0.5 * (frameRect.getP00() + frameRect.getP11());
if (m_visualSettings.m_blankColor != TPixel::Transparent) {
tglColor(m_visualSettings.m_blankColor);
tglFillRect(frameRect);
}
if (!previewer->isFrameReady(row) ||
(app->getCurrentFrame()->isPlaying() && previewer->isBusy())) {
glColor3d(1, 0, 0);
tglDrawRect(frameRect);
tglDrawRect(frameRect.enlarge(5));
}
glPopMatrix();
}
//-----------------------------------------------------------------------------
void SceneViewer::drawOverlay() {
TApp *app = TApp::instance();
// draw camera mask
if (m_referenceMode == CAMERA_REFERENCE && !m_drawCameraTest) {
glPushMatrix();
tglMultMatrix(m_drawCameraAff);
ViewerDraw::drawCameraMask(this);
glPopMatrix();
}
// draw FieldGuide
if (fieldGuideToggle.getStatus()) {
glPushMatrix();
tglMultMatrix(m_drawTableAff);
ViewerDraw::drawFieldGuide();
glPopMatrix();
}
if (!m_drawCameraTest) {
// draw grid & guides
if (viewGuideToggle.getStatus() &&
((m_vRuler && m_vRuler->getGuideCount()) ||
(m_hRuler && m_hRuler->getGuideCount()))) {
glPushMatrix();
tglMultMatrix(getViewMatrix());
ViewerDraw::drawGridAndGuides(
this, (m_draw3DMode) ? m_zoomScale3D : m_viewAff[m_viewMode].det(),
m_vRuler, m_hRuler, false);
glPopMatrix();
}
// draw camera
if (viewCameraToggle.getStatus() && m_drawEditingLevel == false) {
unsigned long f = 0;
if (m_referenceMode == CAMERA_REFERENCE)
f |= ViewerDraw::CAMERA_REFERENCE;
if (m_draw3DMode) f |= ViewerDraw::CAMERA_3D;
if (m_previewMode == SUBCAMERA_PREVIEW || m_editPreviewSubCamera)
f |= ViewerDraw::SUBCAMERA;
if (m_draw3DMode)
ViewerDraw::draw3DCamera(f, m_minZ, m_phi3D);
else {
glPushMatrix();
tglMultMatrix(m_drawCameraAff);
m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio();
ViewerDraw::drawCamera(f, m_pixelSize);
glPopMatrix();
}
}
#ifdef WITH_CANON
if (m_stopMotion->m_liveViewStatus == StopMotion::LiveViewOpen &&
app->getCurrentFrame()->getFrame() ==
m_stopMotion->getXSheetFrameNumber() - 1) {
int x0, x1, y0, y1;
rect().getCoords(&x0, &y0, &x1, &y1);
x0 = (-(x1 / 2)) + 15;
y0 = ((y1 / 2)) - 15;
tglDrawDisk(TPointD(x0, y0), 10);
}
// draw Stop Motion Zoom Box
if (m_stopMotion->m_liveViewStatus == 2 &&
m_stopMotion->m_canon->m_pickLiveViewZoom) {
glPushMatrix();
tglMultMatrix(m_drawCameraAff);
m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio();
TRect rect = m_stopMotion->m_canon->m_zoomRect;
glColor3d(1.0, 0.0, 0.0);
// border
glBegin(GL_LINE_STRIP);
glVertex2d(rect.x0, rect.y0);
glVertex2d(rect.x0, rect.y1 - m_pixelSize);
glVertex2d(rect.x1 - m_pixelSize, rect.y1 - m_pixelSize);
glVertex2d(rect.x1 - m_pixelSize, rect.y0);
glVertex2d(rect.x0, rect.y0);
glEnd();
glPopMatrix();
}
#endif
// safe area
if (safeAreaToggle.getStatus() && m_drawEditingLevel == false &&
!is3DView()) {
glPushMatrix();
tglMultMatrix(m_drawCameraAff);
ViewerDraw::drawSafeArea();
glPopMatrix();
}
// record fps (frame per second)
if (app->getCurrentFrame()->isPlaying())
m_FPS = getActualFrameRate();
else
m_FPS = 0;
if (m_freezedStatus != NO_FREEZED) {
tglColor(TPixel32::Red);
tglDrawText(TPointD(0, 0), "FROZEN");
}
assert(glGetError() == GL_NO_ERROR);
} //! cameraTest
// draw 3d Top/Side Buttons
if (m_draw3DMode && !m_isPicking) {
tglColor(TPixel32::Black);
GLdouble modelView3D[16];
GLdouble projection3D[16];
GLint viewport3D[4];
glGetDoublev(GL_MODELVIEW_MATRIX, modelView3D);
glGetDoublev(GL_PROJECTION_MATRIX, projection3D);
glGetIntegerv(GL_VIEWPORT, viewport3D);
if (m_phi3D > 0) {
T3DPointD topRasterPos3D = computeNew3DPosition(
T3DPointD(500, 500, 1000), TPointD(-10, -10), m_topRasterPos,
modelView3D, projection3D, viewport3D, getDevPixRatio());
glRasterPos3f(topRasterPos3D.x, topRasterPos3D.y, topRasterPos3D.z);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glDrawPixels(m_3DTop->getWrap(), m_3DTop->getLy(), TGL_FMT, TGL_TYPE,
m_3DTop->getRawData());
T3DPointD sideRasterPos3D = computeNew3DPosition(
T3DPointD(-500, -500, 1000), TPointD(-10, -10), m_sideRasterPos,
modelView3D, projection3D, viewport3D, getDevPixRatio());
glRasterPos3f(sideRasterPos3D.x, sideRasterPos3D.y, sideRasterPos3D.z);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glDrawPixels(m_3DSideR->getWrap(), m_3DSideR->getLy(), TGL_FMT, TGL_TYPE,
m_3DSideR->getRawData());
} else {
T3DPointD topRasterPos3D = computeNew3DPosition(
T3DPointD(-500, 500, 1000), TPointD(-10, -10), m_topRasterPos,
modelView3D, projection3D, viewport3D, getDevPixRatio());
glRasterPos3f(topRasterPos3D.x, topRasterPos3D.y, topRasterPos3D.z);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glDrawPixels(m_3DTop->getWrap(), m_3DTop->getLy(), TGL_FMT, TGL_TYPE,
m_3DTop->getRawData());
T3DPointD sideRasterPos3D = computeNew3DPosition(
T3DPointD(500, -500, 1000), TPointD(-10, -10), m_sideRasterPos,
modelView3D, projection3D, viewport3D, getDevPixRatio());
glRasterPos3f(sideRasterPos3D.x, sideRasterPos3D.y, sideRasterPos3D.z);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glDrawPixels(m_3DSideL->getWrap(), m_3DSideL->getLy(), TGL_FMT, TGL_TYPE,
m_3DSideL->getRawData());
}
}
if (m_draw3DMode) {
glDisable(GL_DEPTH_TEST);
glPopMatrix();
assert(glGetError() == GL_NO_ERROR);
}
// draw tool gadgets
TTool *tool = app->getCurrentTool()->getTool();
TXshSimpleLevel *sl = app->getCurrentLevel()->getSimpleLevel();
// Call tool->draw() even if the level is read only (i.e. to show hooks)
if (tool && (tool->isEnabled() || (sl && sl->isReadOnly()))) {
// tool->setViewer(this); // Moved at
// drawBuildVars(), before drawing anything
glPushMatrix();
if (m_draw3DMode) {
mult3DMatrix();
tglMultMatrix(tool->getMatrix());
} else
tglMultMatrix(getViewMatrix() * tool->getMatrix());
if (tool->getToolType() & TTool::LevelTool &&
!app->getCurrentObject()->isSpline())
glScaled(m_dpiScale.x, m_dpiScale.y, 1);
m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio();
tool->draw();
glPopMatrix();
// Used (only in the T_RGBPicker tool) to notify and set the currentColor
// outside the draw() methods:
// using special style there was a conflict between the draw() methods of
// the tool
// and the genaration of the icon inside the style editor (makeIcon()) which
// use
// another glContext
if (tool->getName() == "T_RGBPicker") tool->onImageChanged();
// draw cross at the center of the locator window
if (m_isLocator) {
glColor3d(1.0, 0.0, 0.0);
tglDrawSegment(TPointD(-4, 0), TPointD(5, 0));
tglDrawSegment(TPointD(0, -4), TPointD(0, 5));
}
}
}
//-----------------------------------------------------------------------------
static void drawFpsGraph(int t0, int t1) {
glDisable(GL_BLEND);
static std::deque<std::pair<int, int>> times;
times.push_back(std::make_pair(t0, t1));
while (times.size() > 200) times.pop_front();
double x0 = 10, y0 = 10;
double x1 = x0 + 200;
double y1 = y0 + 150;
glPushMatrix();
glLoadIdentity();
glColor3d(0, 0, 0);
glRectd(x0, y0, x1, y1);
glColor3d(0, 0.5, 1);
glBegin(GL_LINE_STRIP);
glVertex2d(x0, y0);
glVertex2d(x1, y0);
glVertex2d(x1, y1);
glVertex2d(x0, y1);
glVertex2d(x0, y0);
glEnd();
glColor3d(0.5, 0.5, 0.5);
glBegin(GL_LINES);
for (int y = y0 + 5; y < y1; y += 20) {
glVertex2d(x0, y);
glVertex2d(x1, y);
}
for (int i = 0; i < (int)times.size(); i++) {
double x = x1 - i;
glColor3d(1, 0, 0);
glVertex2d(x, y0);
glVertex2d(x, y0 + 5 + times[i].first / 5);
glColor3d(0, 1, 0);
glVertex2d(x, y0 + 5 + times[i].first / 5);
glVertex2d(x, y0 + 5 + times[i].second / 5);
}
glEnd();
glPopMatrix();
}
//-----------------------------------------------------------------------------
//#define FPS_HISTOGRAM
void SceneViewer::paintGL() {
#ifdef _DEBUG
if (!check_framebuffer_status()) {
/* QGLWidget の widget 生成/削除のタイミングで(platform によって?)
* GL_FRAMEBUFFER_UNDEFINED の状態で paintGL() が呼ばれてしまうようだ */
return;
}
#endif
#ifdef MACOSX
// followin lines are necessary to solve a problem on iMac20
// It seems that for some errors in the openGl implementation, buffers are not
// set corretly.
if (m_isMouseEntered && m_forceGlFlush) {
m_isMouseEntered = false;
m_forceGlFlush = false;
glDrawBuffer(GL_FRONT);
glFlush();
glDrawBuffer(GL_BACK);
}
#endif
#ifdef FPS_HISTOGRAM
QTime time;
time.start();
#endif
if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid())
m_fbo->bind();
if (m_hRuler && m_vRuler) {
if (!viewRulerToggle.getStatus() &&
(m_hRuler->isVisible() || m_vRuler->isVisible())) {
m_hRuler->hide();
m_vRuler->hide();
} else if (viewRulerToggle.getStatus() &&
(!m_hRuler->isVisible() || !m_vRuler->isVisible())) {
m_hRuler->show();
m_vRuler->show();
}
}
// Il freezed e' attivo ed e' in stato "normale": mostro l'immagine grabbata.
if (m_freezedStatus == NORMAL_FREEZED) {
assert(!!m_viewGrabImage);
m_viewGrabImage->lock();
glPushMatrix();
glLoadIdentity();
glRasterPos2d(0, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glDrawPixels(m_viewGrabImage->getLx(), m_viewGrabImage->getLy(), TGL_FMT,
TGL_TYPE, m_viewGrabImage->getRawData());
glPopMatrix();
m_viewGrabImage->unlock();
if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid())
m_lutCalibrator->onEndDraw(m_fbo);
return;
}
drawBuildVars();
// This seems not to be necessary for now.
// copyFrontBufferToBackBuffer();
drawEnableScissor();
drawBackground();
if (m_previewMode != FULL_PREVIEW) {
drawCameraStand();
}
if (isPreviewEnabled()) drawPreview();
drawOverlay();
drawDisableScissor();
// Il freezed e' attivo ed e' in stato "update": faccio il grab del viewer.
if (m_freezedStatus == UPDATE_FREEZED) {
m_viewGrabImage = rasterFromQImage(grabFramebuffer());
m_freezedStatus = NORMAL_FREEZED;
}
#ifdef FPS_HISTOGRAM
int t0 = time.elapsed();
glFlush();
glFinish();
int t1 = time.elapsed();
drawFpsGraph(t0, t1);
#endif
// TOfflineGL::setContextManager(0);
if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid())
m_lutCalibrator->onEndDraw(m_fbo);
}
//-----------------------------------------------------------------------------
void SceneViewer::drawScene() {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
int frame = app->getCurrentFrame()->getFrame();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
TRect clipRect = getActualClipRect(getViewMatrix());
clipRect += TPoint(width() * 0.5, height() * 0.5);
ChildStack *childStack = scene->getChildStack();
bool editInPlace = editInPlaceToggle.getStatus() &&
!app->getCurrentFrame()->isEditingLevel();
bool fillFullColorRaster = TXshSimpleLevel::m_fillFullColorRaster;
TXshSimpleLevel::m_fillFullColorRaster = false;
// Guided Drawing Check
int useGuidedDrawing = Preferences::instance()->getGuidedDrawingType();
TTool *tool = app->getCurrentTool()->getTool();
int guidedFrontStroke = tool ? tool->getViewer()->getGuidedFrontStroke() : -1;
int guidedBackStroke = tool ? tool->getViewer()->getGuidedBackStroke() : -1;
m_minZ = 0;
if (is3DView()) {
Stage::OpenGlPainter painter(getViewMatrix(), clipRect, m_visualSettings,
true, false);
painter.enableCamera3D(true);
painter.setPhi(m_phi3D);
int xsheetLevel = 0;
std::pair<TXsheet *, int> xr;
if (editInPlace) {
xr = childStack->getAncestor(frame);
xsheetLevel = childStack->getAncestorCount();
} else
xr = std::make_pair(xsh, frame);
TFrameHandle *frameHandle = TApp::instance()->getCurrentFrame();
Stage::VisitArgs args;
args.m_scene = scene;
args.m_xsh = xr.first;
args.m_row = xr.second;
args.m_col = app->getCurrentColumn()->getColumnIndex();
OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
args.m_osm = &osm;
args.m_camera3d = true;
args.m_xsheetLevel = xsheetLevel;
args.m_currentFrameId =
app->getCurrentXsheet()
->getXsheet()
->getCell(app->getCurrentFrame()->getFrame(), args.m_col)
.getFrameId();
args.m_isGuidedDrawingEnabled = useGuidedDrawing;
args.m_guidedFrontStroke = guidedFrontStroke;
args.m_guidedBackStroke = guidedBackStroke;
// args.m_currentFrameId = app->getCurrentFrame()->getFid();
if (m_stopMotion->m_alwaysUseLiveViewImages &&
m_stopMotion->m_liveViewStatus > 0 &&
frame != m_stopMotion->getXSheetFrameNumber() - 1 &&
m_hasStopMotionImage && !m_stopMotion->m_reviewTimer->isActive()) {
TRaster32P image;
bool hasImage = m_stopMotion->loadLiveViewImage(frame, image);
if (hasImage) {
Stage::Player smPlayer;
double dpiX, dpiY;
m_stopMotionImage->getDpi(dpiX, dpiY);
smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
smPlayer.m_opacity = 255;
smPlayer.m_sl = m_stopMotion->m_sl;
args.m_liveViewImage = static_cast<TRasterImageP>(image);
args.m_liveViewPlayer = smPlayer;
// painter.onRasterImage(static_cast<TRasterImageP>(image).getPointer(),
// smPlayer);
}
}
if ( //! m_stopMotion->m_drawBeneathLevels &&
m_stopMotion->m_liveViewStatus == 2 &&
( //! frameHandle->isPlaying() ||
frame == m_stopMotion->getXSheetFrameNumber() - 1)) {
if (m_hasStopMotionLineUpImage && m_stopMotion->m_showLineUpImage) {
Stage::Player smPlayer;
double dpiX, dpiY;
m_stopMotionLineUpImage->getDpi(dpiX, dpiY);
smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
smPlayer.m_opacity = 255;
smPlayer.m_sl = m_stopMotion->m_sl;
args.m_lineupImage = m_stopMotionLineUpImage;
args.m_lineupPlayer = smPlayer;
// painter.onRasterImage(m_stopMotionLineUpImage.getPointer(),
// smPlayer);
}
if (m_hasStopMotionImage) {
Stage::Player smPlayer;
double dpiX, dpiY;
m_stopMotionImage->getDpi(dpiX, dpiY);
smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
bool hide_opacity = false;
#ifdef WITH_CANON
hide_opacity = m_stopMotion->m_canon->m_zooming ||
m_stopMotion->m_canon->m_pickLiveViewZoom ||
!m_hasStopMotionLineUpImage;
#endif
smPlayer.m_opacity = hide_opacity ? 255.0 : m_stopMotion->getOpacity();
smPlayer.m_sl = m_stopMotion->m_sl;
args.m_liveViewImage = m_stopMotionImage;
args.m_liveViewPlayer = smPlayer;
// painter.onRasterImage(m_stopMotionImage.getPointer(), smPlayer);
}
}
Stage::visit(painter, args);
m_minZ = painter.getMinZ();
} else {
// camera 2D (normale)
TDimension viewerSize(width(), height());
TAffine viewAff = getViewMatrix();
if (editInPlace) {
TAffine aff;
if (scene->getChildStack()->getAncestorAffine(aff, frame))
viewAff = viewAff * aff.inv();
}
m_visualSettings.m_showBBox = viewBBoxToggle.getStatus();
Stage::RasterPainter painter(viewerSize, viewAff, clipRect,
m_visualSettings, true);
// darken blended view mode for viewing the non-cleanuped and stacked
// drawings
painter.setRasterDarkenBlendedView(
Preferences::instance()
->isShowRasterImagesDarkenBlendedInViewerEnabled());
TFrameHandle *frameHandle = TApp::instance()->getCurrentFrame();
if (app->getCurrentFrame()->isEditingLevel()) {
Stage::visit(painter, app->getCurrentLevel()->getLevel(),
app->getCurrentFrame()->getFid(),
app->getCurrentOnionSkin()->getOnionSkinMask(),
frameHandle->isPlaying(), useGuidedDrawing, guidedBackStroke,
guidedFrontStroke);
} else {
std::pair<TXsheet *, int> xr;
int xsheetLevel = 0;
if (editInPlace) {
xr = scene->getChildStack()->getAncestor(frame);
xsheetLevel = scene->getChildStack()->getAncestorCount();
} else
xr = std::make_pair(xsh, frame);
Stage::VisitArgs args;
args.m_scene = scene;
args.m_xsh = xr.first;
args.m_row = xr.second;
args.m_col = app->getCurrentColumn()->getColumnIndex();
OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
args.m_osm = &osm;
args.m_xsheetLevel = xsheetLevel;
args.m_isPlaying = frameHandle->isPlaying();
args.m_currentFrameId =
app->getCurrentXsheet()
->getXsheet()
->getCell(app->getCurrentFrame()->getFrame(), args.m_col)
.getFrameId();
args.m_isGuidedDrawingEnabled = useGuidedDrawing;
args.m_guidedFrontStroke = guidedFrontStroke;
args.m_guidedBackStroke = guidedBackStroke;
if (m_stopMotion->m_alwaysUseLiveViewImages &&
m_stopMotion->m_liveViewStatus > 0 &&
frame != m_stopMotion->getXSheetFrameNumber() - 1 &&
m_hasStopMotionImage && !m_stopMotion->m_reviewTimer->isActive()) {
TRaster32P image;
bool hasImage = m_stopMotion->loadLiveViewImage(frame, image);
if (hasImage) {
Stage::Player smPlayer;
double dpiX, dpiY;
m_stopMotionImage->getDpi(dpiX, dpiY);
smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
smPlayer.m_opacity = 255;
smPlayer.m_sl = m_stopMotion->m_sl;
args.m_liveViewImage = static_cast<TRasterImageP>(image);
args.m_liveViewPlayer = smPlayer;
// painter.onRasterImage(static_cast<TRasterImageP>(image).getPointer(),
// smPlayer);
}
}
if ( //! m_stopMotion->m_drawBeneathLevels &&
m_stopMotion->m_liveViewStatus == 2 &&
( //! frameHandle->isPlaying() ||
frame == m_stopMotion->getXSheetFrameNumber() - 1)) {
if (m_hasStopMotionLineUpImage && m_stopMotion->m_showLineUpImage) {
Stage::Player smPlayer;
double dpiX, dpiY;
m_stopMotionLineUpImage->getDpi(dpiX, dpiY);
smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
smPlayer.m_opacity = 255;
smPlayer.m_sl = m_stopMotion->m_sl;
args.m_lineupImage = m_stopMotionLineUpImage;
args.m_lineupPlayer = smPlayer;
// painter.onRasterImage(m_stopMotionLineUpImage.getPointer(),
// smPlayer);
}
if (m_hasStopMotionImage) {
Stage::Player smPlayer;
double dpiX, dpiY;
m_stopMotionImage->getDpi(dpiX, dpiY);
smPlayer.m_dpiAff = TScale(Stage::inch / dpiX, Stage::inch / dpiY);
bool hide_opacity = false;
#ifdef WITH_CANON
hide_opacity = m_stopMotion->m_canon->m_zooming ||
m_stopMotion->m_canon->m_pickLiveViewZoom ||
!m_hasStopMotionLineUpImage;
#endif
smPlayer.m_opacity =
hide_opacity ? 255.0 : m_stopMotion->getOpacity();
smPlayer.m_sl = m_stopMotion->m_sl;
args.m_liveViewImage = m_stopMotionImage;
args.m_liveViewPlayer = smPlayer;
// painter.onRasterImage(m_stopMotionImage.getPointer(), smPlayer);
}
}
Stage::visit(painter, args);
}
assert(glGetError() == 0);
painter.flushRasterImages();
TXshSimpleLevel::m_fillFullColorRaster = fillFullColorRaster;
assert(glGetError() == 0);
if (m_viewMode != LEVEL_VIEWMODE)
drawSpline(getViewMatrix(), clipRect,
m_referenceMode == CAMERA3D_REFERENCE, m_pixelSize);
assert(glGetError() == 0);
// gather animated guide strokes' bounding boxes
// it is used for updating viewer next time
std::vector<TStroke *> guidedStrokes = painter.getGuidedStrokes();
for (auto itr = guidedStrokes.begin(); itr != guidedStrokes.end(); ++itr) {
m_guidedDrawingBBox += (*itr)->getBBox();
}
}
}
//------------------------------------------------------------------------------
void SceneViewer::mult3DMatrix() {
glTranslated(m_pan3D.x, m_pan3D.y, 0);
glScaled(m_zoomScale3D, m_zoomScale3D, 1);
glRotated(m_theta3D, 1, 0, 0);
glRotated(m_phi3D, 0, 1, 0);
}
//-----------------------------------------------------------------------------
double SceneViewer::projectToZ(const TPointD &delta) {
glPushMatrix();
mult3DMatrix();
GLint viewport[4];
double modelview[16], projection[16];
glGetIntegerv(GL_VIEWPORT, viewport);
for (int i = 0; i < 16; i++)
projection[i] = (double)m_projectionMatrix.constData()[i];
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
double ax, ay, az, bx, by, bz;
gluProject(0, 0, 0, modelview, projection, viewport, &ax, &ay, &az);
gluProject(0, 0, 1, modelview, projection, viewport, &bx, &by, &bz);
glPopMatrix();
TPointD zdir(bx - ax, by - ay);
double zdirLength2 = norm2(zdir);
if (zdirLength2 > 0.0) {
double dz = (delta * zdir) / zdirLength2;
return dz;
} else
return 0.0;
}
//-----------------------------------------------------------------------------
TRect SceneViewer::getActualClipRect(const TAffine &aff) {
TDimensionD viewerSize(width(), height());
TRectD clipRect(viewerSize);
if (is3DView()) {
TPointD p00 = winToWorld(clipRect.getP00());
TPointD p01 = winToWorld(clipRect.getP01());
TPointD p10 = winToWorld(clipRect.getP10());
TPointD p11 = winToWorld(clipRect.getP11());
clipRect = TRectD(TPointD(std::min(p00.x, p01.x), std::min(p00.y, p10.y)),
TPointD(std::max(p11.x, p10.x), std::max(p11.y, p01.y)));
}
// this condition will catch both cases of m_clipRect == empty and
// m_clipRect == InvalidateAllRect
else if (m_clipRect.isEmpty())
clipRect -= TPointD(viewerSize.lx / 2, viewerSize.ly / 2);
else {
TRectD app = aff * (m_clipRect.enlarge(3));
clipRect =
TRectD(tceil(app.x0), tceil(app.y0), tfloor(app.x1), tfloor(app.y1));
}
return convert(clipRect);
}
//-----------------------------------------------------------------------------
TAffine SceneViewer::getViewMatrix() const {
int viewMode = TApp::instance()->getCurrentFrame()->isEditingLevel()
? LEVEL_VIEWMODE
: SCENE_VIEWMODE;
if (is3DView()) return TAffine();
if (m_referenceMode == CAMERA_REFERENCE) {
int frame = TApp::instance()->getCurrentFrame()->getFrame();
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TAffine aff = xsh->getCameraAff(frame);
return m_viewAff[viewMode] * aff.inv();
} else
return m_viewAff[viewMode];
}
//-----------------------------------------------------------------------------
TAffine SceneViewer::getSceneMatrix() const {
int viewMode = TApp::instance()->getCurrentFrame()->isEditingLevel()
? LEVEL_VIEWMODE
: SCENE_VIEWMODE;
if (is3DView()) return TAffine();
return m_viewAff[viewMode];
}
//-----------------------------------------------------------------------------
void SceneViewer::setViewMatrix(const TAffine &aff, int viewMode) {
m_viewAff[viewMode] = aff;
// In case the previewer is on, request a delayed update
if (m_previewMode != NO_PREVIEW) requestTimedRefresh();
}
//-----------------------------------------------------------------------------
bool SceneViewer::is3DView() const {
bool isCameraTest = CameraTestCheck::instance()->isEnabled();
return (m_referenceMode == CAMERA3D_REFERENCE && !isCameraTest);
}
//-----------------------------------------------------------------------------
void SceneViewer::invalidateAll() {
m_clipRect = InvalidateAllRect;
update();
if (m_vRuler) m_vRuler->update();
if (m_hRuler) m_hRuler->update();
}
//-----------------------------------------------------------------------------
/*! Pan the viewer by using "navigator" (red rectangle) in level strip
*/
void SceneViewer::navigatorPan(const QPoint &delta) {
panQt(delta);
m_pos += delta;
}
//-----------------------------------------------------------------------------
void SceneViewer::GLInvalidateAll() {
m_clipRect = InvalidateAllRect;
update();
if (m_vRuler) m_vRuler->update();
if (m_hRuler) m_hRuler->update();
}
//-----------------------------------------------------------------------------
void SceneViewer::GLInvalidateRect(const TRectD &rect) {
// in case that GLInvalidateAll is called just before coming here,
// ignore the clip rect and refresh entire viewer
if (m_clipRect == InvalidateAllRect)
return;
else if (rect.isEmpty())
m_clipRect = InvalidateAllRect;
else {
m_clipRect += rect;
if (!m_guidedDrawingBBox.isEmpty()) {
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
TPointD topLeft = tool->getMatrix() * m_guidedDrawingBBox.getP00();
TPointD bottomRight = tool->getMatrix() * m_guidedDrawingBBox.getP11();
m_clipRect += TRectD(topLeft, bottomRight);
}
}
update();
if (m_vRuler) m_vRuler->update();
if (m_hRuler) m_hRuler->update();
m_guidedDrawingBBox.empty();
}
//-----------------------------------------------------------------------------
// delta.x: right panning, pixel; delta.y: down panning, pixel
void SceneViewer::panQt(const QPointF &delta) {
if (delta == QPointF()) return;
if (is3DView())
m_pan3D += TPointD(delta.x(), -delta.y());
else {
// TAffine &viewAff = m_viewAff[m_viewMode];
// viewAff = TTranslation(delta.x(), -delta.y()) * viewAff;
setViewMatrix(TTranslation(delta.x(), -delta.y()) * m_viewAff[m_viewMode],
m_viewMode);
}
invalidateAll();
emit refreshNavi();
}
//-----------------------------------------------------------------------------
void SceneViewer::zoomQt(bool forward, bool reset) {
TPointD delta(m_lastMousePos.x() - width() / 2,
-m_lastMousePos.y() + height() / 2);
if (is3DView()) {
if (reset || ((m_zoomScale3D < 500 || !forward) &&
(m_zoomScale3D > 0.01 || forward))) {
double oldZoomScale = m_zoomScale3D;
m_zoomScale3D = reset ? 1 : ImageUtils::getQuantizedZoomFactor(
m_zoomScale3D, forward);
m_pan3D = -(m_zoomScale3D / oldZoomScale) * -m_pan3D;
}
} else {
// a factor for getting pixel-based zoom ratio
double dpiFactor = getDpiFactor();
// when showing the viewer with full-screen mode,
// add a zoom factor which can show image fitting with the screen size
double zoomScaleFittingWithScreen = 0.0f;
if (dpiFactor != 1.0) {
// check if the viewer is in full screen mode
ImageUtils::FullScreenWidget *fsWidget =
dynamic_cast<ImageUtils::FullScreenWidget *>(parentWidget());
if (fsWidget && (fsWidget->windowState() & Qt::WindowFullScreen) != 0)
zoomScaleFittingWithScreen = getZoomScaleFittingWithScreen();
}
int i;
for (i = 0; i < 2; i++) {
TAffine &viewAff = m_viewAff[i];
if (m_isFlippedX) viewAff = viewAff * TScale(-1, 1);
if (m_isFlippedX) viewAff = viewAff * TScale(1, -1);
double scale2 = std::abs(viewAff.det());
if (m_isFlippedX) viewAff = viewAff * TScale(-1, 1);
if (m_isFlippedX) viewAff = viewAff * TScale(1, -1);
if (reset || ((scale2 < 100000 || !forward) &&
(scale2 > 0.001 * 0.05 || forward))) {
double oldZoomScale = sqrt(scale2) * dpiFactor;
double zoomScale = reset ? 1 : ImageUtils::getQuantizedZoomFactor(
oldZoomScale, forward);
// threshold value -0.001 is intended to absorb the error of calculation
if ((oldZoomScale - zoomScaleFittingWithScreen) *
(zoomScale - zoomScaleFittingWithScreen) <
-0.001)
zoomScale = zoomScaleFittingWithScreen;
// zoom center = viewer center
if (Preferences::instance()->getViewerZoomCenter())
setViewMatrix(TScale(zoomScale / oldZoomScale) * viewAff, i);
// zoom center = mouse pos
else
setViewMatrix(TTranslation(delta) * TScale(zoomScale / oldZoomScale) *
TTranslation(-delta) * viewAff,
i);
}
}
}
GLInvalidateAll();
emit onZoomChanged();
}
//-----------------------------------------------------------------------------
/*! a factor for getting pixel-based zoom ratio
*/
double SceneViewer::getDpiFactor() {
// When the current unit is "pixels", always use a standard dpi
double cameraDpi = TApp::instance()
->getCurrentScene()
->getScene()
->getCurrentCamera()
->getDpi()
.x;
if (Preferences::instance()->getPixelsOnly()) {
return Stage::inch / Stage::standardDpi;
}
// When preview mode, use a camera DPI
else if (isPreviewEnabled()) {
return Stage::inch / cameraDpi;
}
// When level editing mode, use an image DPI
else if (TApp::instance()->getCurrentFrame()->isEditingLevel()) {
TXshSimpleLevel *sl;
sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl) return Stage::inch / cameraDpi;
if (sl->getType() == PLI_XSHLEVEL) return Stage::inch / cameraDpi;
if (sl->getImageDpi() != TPointD())
return Stage::inch / sl->getImageDpi().x;
if (sl->getDpi() != TPointD()) return Stage::inch / sl->getDpi().x;
// no valid dpi, use camera dpi
return Stage::inch / cameraDpi;
}
// When the special case in the scene editing mode:
// If the option "ActualPixelViewOnSceneEditingMode" is ON,
// use current level's DPI set in the level settings.
else if (Preferences::instance()
->isActualPixelViewOnSceneEditingModeEnabled()) {
if (CleanupPreviewCheck::instance()->isEnabled() ||
CameraTestCheck::instance()->isEnabled()) {
double cleanupCameraDpi = TApp::instance()
->getCurrentScene()
->getScene()
->getProperties()
->getCleanupParameters()
->m_camera.getDpi()
.x;
return Stage::inch / cleanupCameraDpi;
} else {
TXshSimpleLevel *sl;
sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl) return Stage::inch / cameraDpi;
if (sl->getType() == PLI_XSHLEVEL) return Stage::inch / cameraDpi;
if (sl->getDpi() == TPointD()) return Stage::inch / cameraDpi;
// use default value for the argument of getDpi() (=TFrameId::NO_FRAME
// so that the dpi of the first frame in the level will be returned.
return Stage::inch / sl->getDpi().x;
}
}
// When the scene editing mode without any option, use the camera dpi
else {
return Stage::inch / cameraDpi;
}
}
//-----------------------------------------------------------------------------
/*! when showing the viewer with full-screen mode,
add a zoom factor which can show image fitting with the screen size
*/
double SceneViewer::getZoomScaleFittingWithScreen() {
TDimension imgSize;
// get the image size to be rendered
if (isPreviewEnabled())
imgSize = TApp::instance()
->getCurrentScene()
->getScene()
->getCurrentCamera()
->getRes();
else if (TApp::instance()->getCurrentFrame()->isEditingLevel()) {
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl || sl->getType() == PLI_XSHLEVEL || sl->getImageDpi() == TPointD())
return 0.0;
imgSize = sl->getResolution();
} else if (Preferences::instance()
->isActualPixelViewOnSceneEditingModeEnabled() &&
!CleanupPreviewCheck::instance()->isEnabled() &&
!CameraTestCheck::instance()->isEnabled()) {
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl || sl->getType() == PLI_XSHLEVEL || sl->getDpi() == TPointD())
return 0.0;
imgSize = sl->getResolution();
} else // SCENE_VIEWMODE
return 0.0;
// add small margin on the edge of the image
int margin = 20;
// get the desktop resolution
QRect rec = QApplication::desktop()->screenGeometry();
// fit to either direction
int moni_x = rec.width() - (margin * 2);
int moni_y = rec.height() - (margin * 2);
return std::min((double)moni_x / (double)imgSize.lx,
(double)moni_y / (double)imgSize.ly);
}
//-----------------------------------------------------------------------------
// center: window coordinate, pixels, topleft origin
void SceneViewer::zoomQt(const QPoint &center, double factor) {
if (factor == 1.0) return;
TPointD delta(center.x() - width() / 2, -center.y() + height() / 2);
double oldZoomScale = m_zoomScale3D;
if (is3DView()) {
if ((m_zoomScale3D < 500 || factor < 1) &&
(m_zoomScale3D > 0.01 || factor > 1)) {
m_zoomScale3D *= factor;
m_pan3D = -(m_zoomScale3D / oldZoomScale) * (delta - m_pan3D) + delta;
}
} else {
int i;
for (i = 0; i < 2; i++) {
TAffine &viewAff = m_viewAff[i];
double scale2 = fabs(viewAff.det());
if ((scale2 < 100000 || factor < 1) &&
(scale2 > 0.001 * 0.05 || factor > 1)) {
if (i == m_viewMode) {
// viewAff = TTranslation(delta) * TScale(factor) *
// TTranslation(-delta) * viewAff;
setViewMatrix(TTranslation(delta) * TScale(factor) *
TTranslation(-delta) * viewAff,
i);
} else {
// viewAff = TScale(factor) * viewAff;
setViewMatrix(TScale(factor) * viewAff, i);
}
}
}
}
GLInvalidateAll();
emit onZoomChanged();
}
void SceneViewer::zoom(const TPointD &center, double factor) {
zoomQt(QPoint(center.x, height() - center.y), factor);
}
//-----------------------------------------------------------------------------
void SceneViewer::flipX() {
double flipAngle0 = (m_rotationAngle[0] * -1) * 2;
double flipAngle1 = (m_rotationAngle[1] * -1) * 2;
m_rotationAngle[0] += flipAngle0;
m_rotationAngle[1] += flipAngle1;
if (m_isFlippedX != m_isFlippedY) {
flipAngle0 = -flipAngle0;
flipAngle1 = -flipAngle1;
}
m_viewAff[0] = m_viewAff[0] * TRotation(flipAngle0) * TScale(-1, 1);
m_viewAff[1] = m_viewAff[1] * TRotation(flipAngle1) * TScale(-1, 1);
m_viewAff[0].a13 *= -1;
m_viewAff[1].a13 *= -1;
m_isFlippedX = !m_isFlippedX;
invalidateAll();
emit onZoomChanged();
emit onFlipHChanged(m_isFlippedX);
}
//-----------------------------------------------------------------------------
void SceneViewer::flipY() {
double flipAngle0 = (m_rotationAngle[0] * -1) * 2;
double flipAngle1 = (m_rotationAngle[1] * -1) * 2;
m_rotationAngle[0] += flipAngle0;
m_rotationAngle[1] += flipAngle1;
if (m_isFlippedX != m_isFlippedY) {
flipAngle0 = -flipAngle0;
flipAngle1 = -flipAngle1;
}
m_viewAff[0] = m_viewAff[0] * TRotation(flipAngle0) * TScale(1, -1);
m_viewAff[1] = m_viewAff[1] * TRotation(flipAngle1) * TScale(1, -1);
m_viewAff[0].a23 *= -1;
m_viewAff[1].a23 *= -1;
m_isFlippedY = !m_isFlippedY;
invalidateAll();
emit onZoomChanged();
emit onFlipVChanged(m_isFlippedY);
}
//-----------------------------------------------------------------------------
void SceneViewer::zoomIn() {
m_lastMousePos = rect().center();
zoomQt(true, false);
}
//-----------------------------------------------------------------------------
void SceneViewer::zoomOut() {
m_lastMousePos = rect().center();
zoomQt(false, false);
}
//-----------------------------------------------------------------------------
void SceneViewer::rotate(const TPointD &center, double angle) {
if (angle == 0) return;
if (m_isFlippedX != m_isFlippedY) angle = -angle;
m_rotationAngle[m_viewMode] += angle;
TPointD realCenter = m_viewAff[m_viewMode] * center;
setViewMatrix(TRotation(realCenter, angle) * m_viewAff[m_viewMode],
m_viewMode);
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::rotate3D(double dPhi, double dTheta) {
if (dPhi == 0 && dTheta == 0) return;
m_phi3D = (float)tcrop(m_phi3D + dPhi, -90.0, 90.0);
m_theta3D = (float)tcrop(m_theta3D + dTheta, 0.0, 90.0);
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::regeneratePreview() {
Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)->clear();
update();
}
//-----------------------------------------------------------------------------
void SceneViewer::regeneratePreviewFrame() {
Previewer::instance(m_previewMode == SUBCAMERA_PREVIEW)
->clear(TApp::instance()->getCurrentFrame()->getFrame());
update();
}
//-----------------------------------------------------------------------------
void SceneViewer::swapCompared() {
m_compareSettings.m_swapCompared = !m_compareSettings.m_swapCompared;
update();
}
//-----------------------------------------------------------------------------
void SceneViewer::fitToCamera() {
// Reset the view scale to 1:1.
bool tempIsFlippedX = m_isFlippedX;
bool tempIsFlippedY = m_isFlippedY;
resetSceneViewer();
m_isFlippedX = tempIsFlippedX;
m_isFlippedY = tempIsFlippedY;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int frame = TApp::instance()->getCurrentFrame()->getFrame();
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
TStageObject *camera = xsh->getStageObject(cameraId);
TAffine cameraPlacement = camera->getPlacement(frame);
double cameraZ = camera->getZ(frame);
TAffine cameraAff =
getViewMatrix() * cameraPlacement * TScale((1000 + cameraZ) / 1000);
QRect viewRect = rect();
TRectD cameraRect = ViewerDraw::getCameraRect();
TPointD P00 = cameraAff * cameraRect.getP00();
TPointD P10 = cameraAff * cameraRect.getP10();
TPointD P01 = cameraAff * cameraRect.getP01();
TPointD P11 = cameraAff * cameraRect.getP11();
TPointD p0 = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}),
std::min({P00.y, P01.y, P10.y, P11.y}));
TPointD p1 = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}),
std::max({P00.y, P01.y, P10.y, P11.y}));
cameraRect = TRectD(p0.x, p0.y, p1.x, p1.y);
// Pan
if (!is3DView()) {
TPointD cameraCenter = (cameraRect.getP00() + cameraRect.getP11()) * 0.5;
panQt(QPoint(-cameraCenter.x, cameraCenter.y));
}
double xratio = (double)viewRect.width() / cameraRect.getLx();
double yratio = (double)viewRect.height() / cameraRect.getLy();
double ratio = std::min(xratio, yratio);
if (ratio == 0.0) return;
if (tempIsFlippedX)
setViewMatrix(TScale(-1, 1) * m_viewAff[m_viewMode], m_viewMode);
if (tempIsFlippedY)
setViewMatrix(TScale(1, -1) * m_viewAff[m_viewMode], m_viewMode);
// Scale and center on the center of \a rect.
QPoint c = viewRect.center();
zoom(TPointD(c.x(), c.y()), ratio);
emit onFlipHChanged(m_isFlippedX);
emit onFlipVChanged(m_isFlippedY);
}
//-----------------------------------------------------------------------------
void SceneViewer::fitToCameraOutline() {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int frame = TApp::instance()->getCurrentFrame()->getFrame();
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
TStageObject *camera = xsh->getStageObject(cameraId);
TAffine cameraPlacement = camera->getPlacement(frame);
double cameraZ = camera->getZ(frame);
TAffine cameraAff =
getViewMatrix() * cameraPlacement * TScale((1000 + cameraZ) / 1000);
QRect viewRect = rect();
TRectD cameraRect = ViewerDraw::getCameraRect();
TPointD P00 = cameraAff * cameraRect.getP00();
TPointD P10 = cameraAff * cameraRect.getP10();
TPointD P01 = cameraAff * cameraRect.getP01();
TPointD P11 = cameraAff * cameraRect.getP11();
TPointD p0 = TPointD(std::min({P00.x, P01.x, P10.x, P11.x}),
std::min({P00.y, P01.y, P10.y, P11.y}));
TPointD p1 = TPointD(std::max({P00.x, P01.x, P10.x, P11.x}),
std::max({P00.y, P01.y, P10.y, P11.y}));
cameraRect = TRectD(p0.x, p0.y, p1.x, p1.y);
// Pan
if (!is3DView()) {
TPointD cameraCenter = (cameraRect.getP00() + cameraRect.getP11()) * 0.5;
panQt(QPoint(-cameraCenter.x, cameraCenter.y));
}
double xratio = (double)viewRect.width() / cameraRect.getLx();
double yratio = (double)viewRect.height() / cameraRect.getLy();
double ratio = std::min(xratio, yratio);
if (ratio == 0.0) return;
// Scale and center on the center of \a rect.
QPoint c = viewRect.center();
zoom(TPointD(c.x(), c.y()), ratio);
zoom(TPointD(c.x(), c.y()), 0.95);
}
//-----------------------------------------------------------------------------
void SceneViewer::resetSceneViewer() {
m_visualSettings.m_sceneProperties =
TApp::instance()->getCurrentScene()->getScene()->getProperties();
for (int i = 0; i < m_viewAff.size(); ++i) {
setViewMatrix(getNormalZoomScale(), i);
m_rotationAngle[i] = 0.0;
}
m_pos = QPoint(0, 0);
m_pan3D = TPointD(0, 0);
m_zoomScale3D = 0.1;
m_theta3D = 20;
m_phi3D = 30;
m_isFlippedX = false;
m_isFlippedY = false;
fitToCameraOutline();
emit onZoomChanged();
emit onFlipHChanged(m_isFlippedX);
emit onFlipVChanged(m_isFlippedY);
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::resetZoom() {
TPointD realCenter(m_viewAff[m_viewMode].a13, m_viewAff[m_viewMode].a23);
TAffine aff =
getNormalZoomScale() * TRotation(realCenter, m_rotationAngle[m_viewMode]);
aff.a13 = realCenter.x;
aff.a23 = realCenter.y;
if (m_isFlippedX) aff = aff * TScale(-1, 1);
if (m_isFlippedY) aff = aff * TScale(1, -1);
setViewMatrix(aff, m_viewMode);
invalidateAll();
emit onZoomChanged();
}
//-----------------------------------------------------------------------------
void SceneViewer::resetRotation() {
double reverseRotatation = m_rotationAngle[m_viewMode] * -1;
if (m_isFlippedX) reverseRotatation *= -1;
if (m_isFlippedY) reverseRotatation *= -1;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
TPointD center = m_viewAff[m_viewMode].inv() * TPointD(0, 0);
if (tool->getName() == "T_Rotate" &&
tool->getProperties(0)
->getProperty("Rotate On Camera Center")
->getValueAsString() == "1")
center = TPointD(0, 0);
rotate(center, reverseRotatation);
}
//-----------------------------------------------------------------------------
void SceneViewer::resetPosition() {
m_viewAff[m_viewMode].a13 = 0.0;
m_viewAff[m_viewMode].a23 = 0.0;
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::setActualPixelSize() {
TApp *app = TApp::instance();
TXshLevel *l = app->getCurrentLevel()->getLevel();
TXshSimpleLevel *sl = l ? l->getSimpleLevel() : 0;
if (!sl) return;
TFrameId fid(app->getCurrentFrame()->getFid());
TPointD dpi;
if (CleanupPreviewCheck::instance()->isEnabled()) {
// The previewed cleanup image has no image infos yet - retrieve the dpi
// through
// the cleanup camera data
CleanupParameters *cleanupParams = app->getCurrentScene()
->getScene()
->getProperties()
->getCleanupParameters();
TDimension dim(0, 0);
cleanupParams->getOutputImageInfo(dim, dpi.x, dpi.y);
} else
dpi = sl->getDpi(fid);
const double inch = Stage::inch;
TAffine tempAff = getNormalZoomScale();
if (m_isFlippedX) tempAff = tempAff * TScale(-1, 1);
if (m_isFlippedY) tempAff = tempAff * TScale(1, -1);
TPointD tempScale = dpi;
if (m_isFlippedX) tempScale.x = -tempScale.x;
if (m_isFlippedY) tempScale.y = -tempScale.y;
for (int i = 0; i < m_viewAff.size(); ++i)
setViewMatrix(dpi == TPointD(0, 0) ? tempAff : TScale(tempScale.x / inch,
tempScale.y / inch),
i);
m_pos = QPoint(0, 0);
m_pan3D = TPointD(0, 0);
m_zoomScale3D = 0.1;
m_theta3D = 20;
m_phi3D = 30;
emit onZoomChanged();
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::onLevelChanged() {
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool) {
TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel();
if (level && level->getSimpleLevel())
m_dpiScale =
getCurrentDpiScale(level->getSimpleLevel(), tool->getCurrentFid());
else
m_dpiScale = TPointD(1, 1);
}
}
//-----------------------------------------------------------------------------
/*! when level is switched, update m_dpiScale in order to show white background
* for Ink&Paint work properly
*/
void SceneViewer::onLevelSwitched() {
invalidateToolStatus();
TApp *app = TApp::instance();
TTool *tool = app->getCurrentTool()->getTool();
TXshLevel *level = app->getCurrentLevel()->getLevel();
if (level && level->getSimpleLevel())
m_dpiScale =
getCurrentDpiScale(level->getSimpleLevel(), tool->getCurrentFid());
else
m_dpiScale = TPointD(1, 1);
}
//-----------------------------------------------------------------------------
void SceneViewer::onXsheetChanged() {
m_forceGlFlush = true;
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool && tool->isEnabled()) tool->updateMatrix();
onLevelChanged();
GLInvalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::onObjectSwitched() {
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool && tool->isEnabled()) tool->updateMatrix();
onLevelChanged();
GLInvalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::onSceneChanged() {
onLevelChanged();
GLInvalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::onFrameSwitched() {
invalidateToolStatus();
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool && tool->isEnabled()) {
tool->setViewer(this);
tool->updateMatrix();
tool->onEnter();
}
GLInvalidateAll();
}
//-----------------------------------------------------------------------------
/*! when tool options are changed, update tooltip immediately
*/
void SceneViewer::onToolChanged() {
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool) setToolCursor(this, tool->getCursorId());
GLInvalidateAll();
}
//-----------------------------------------------------------------------------
int SceneViewer::pick(const TPointD &point) {
// pick is typically called in a mouse event handler.
// QGLWidget::makeCurrent() is not automatically called in these events.
// (to exploit the bug: open the FxEditor preview and then select the edit
// tool)
m_isPicking = true;
makeCurrent();
assert(glGetError() == GL_NO_ERROR);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
std::array<GLuint, 512> selectBuffer;
glSelectBuffer(selectBuffer.size(), selectBuffer.data());
glRenderMode(GL_SELECT);
// definisco la matrice di proiezione
glMatrixMode(GL_PROJECTION);
GLdouble mat[16];
glGetDoublev(GL_PROJECTION_MATRIX, mat);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(point.x, point.y, 5, 5, viewport);
glMultMatrixd(mat);
assert(glGetError() == GL_NO_ERROR);
// disegno la scena
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glInitNames();
assert(glGetError() == GL_NO_ERROR);
// WARNING: We have to draw the scene in CAMERASTAND mode. Observe that the
// preview mode may
// invoke event processing - therefore triggering other pick events while in
// GL_SELECT
// render mode...
int previewMode = m_previewMode;
m_previewMode = NO_PREVIEW;
// OPTIMIZATION / QUICK FIX
// A 1x1 clipping rect around the picked pos can very well be used instead of
// redrawing
// the *entire viewer*.
// This is needed especially since some graphic cards (all NVidias we have
// tested) are
// very slow otherwise. This could be due to this particular rendering mode -
// or because
// we could be painting OUTSIDE a paintEvent()...
TRectD oldClipRect(m_clipRect);
m_clipRect = TRectD(point.x, point.y, point.x + 1, point.y + 1);
paintGL(); // draw identifiable objects
m_clipRect = oldClipRect;
m_previewMode = previewMode;
assert(glGetError() == GL_NO_ERROR);
glPopMatrix();
// rimetto a posto la matrice di proiezione
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
assert(glGetError() == GL_NO_ERROR);
// conto gli hits
int ret = -1;
int hitCount = glRenderMode(GL_RENDER);
GLuint *p = selectBuffer.data();
for (int i = 0; i < hitCount; ++i) {
GLuint nameCount = *p++;
GLuint zmin = *p++;
GLuint zmax = *p++;
if (nameCount > 0) {
GLuint name = *p;
ret = name; // items.push_back(PickItem(name, zmin, zmax));
}
p += nameCount;
}
assert(glGetError() == GL_NO_ERROR);
m_isPicking = false;
return ret;
}
//-----------------------------------------------------------------------------
int SceneViewer::posToColumnIndex(const TPointD &p, double distance,
bool includeInvisible) const {
std::vector<int> ret;
posToColumnIndexes(p, ret, distance, includeInvisible);
return ret.empty() ? -1 : ret.back();
}
//-----------------------------------------------------------------------------
void SceneViewer::posToColumnIndexes(const TPointD &p,
std::vector<int> &indexes, double distance,
bool includeInvisible) const {
int oldRasterizePli = TXshSimpleLevel::m_rasterizePli;
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int frame = app->getCurrentFrame()->getFrame();
int currentColumnIndex = app->getCurrentColumn()->getColumnIndex();
OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
TPointD pos = TPointD(p.x - width() / 2, p.y - height() / 2);
Stage::Picker picker(getViewMatrix(), pos, m_visualSettings);
picker.setDistance(distance);
TXshSimpleLevel::m_rasterizePli = 0;
Stage::VisitArgs args;
args.m_scene = scene;
args.m_xsh = xsh;
args.m_row = frame;
args.m_col = currentColumnIndex;
args.m_osm = &osm;
args.m_onlyVisible = includeInvisible;
Stage::visit(picker, args); /*
picker,
scene,
xsh,
frame,
currentColumnIndex,
osm,
false,
0,
false,
includeInvisible);
*/
TXshSimpleLevel::m_rasterizePli = oldRasterizePli;
picker.getColumnIndexes(indexes);
}
//-----------------------------------------------------------------------------
int SceneViewer::posToRow(const TPointD &p, double distance,
bool includeInvisible, bool currentColumnOnly) const {
int oldRasterizePli = TXshSimpleLevel::m_rasterizePli;
TApp *app = TApp::instance();
OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
TPointD pos = TPointD(p.x - width() / 2, p.y - height() / 2);
Stage::Picker picker(getViewMatrix(), pos, m_visualSettings);
picker.setDistance(distance);
if (app->getCurrentFrame()->isEditingLevel()) {
Stage::visit(picker, app->getCurrentLevel()->getLevel(),
app->getCurrentFrame()->getFid(), osm,
app->getCurrentFrame()->isPlaying(), false);
} else {
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
int frame = app->getCurrentFrame()->getFrame();
int currentColumnIndex = app->getCurrentColumn()->getColumnIndex();
TXshSimpleLevel::m_rasterizePli = 0;
Stage::VisitArgs args;
args.m_scene = scene;
args.m_xsh = xsh;
args.m_row = frame;
args.m_col = currentColumnIndex;
args.m_osm = &osm;
args.m_onlyVisible = includeInvisible;
if (currentColumnOnly) picker.setCurrentColumnIndex(currentColumnIndex);
Stage::visit(picker, args);
}
TXshSimpleLevel::m_rasterizePli = oldRasterizePli;
return picker.getRow();
}
//-----------------------------------------------------------------------------
void drawSpline(const TAffine &viewMatrix, const TRect &clipRect, bool camera3d,
double pixelsize) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
TStageObjectId objId = TApp::instance()->getCurrentObject()->getObjectId();
TStageObject *pegbar =
objId != TStageObjectId::NoneId ? xsh->getStageObject(objId) : 0;
const TStroke *stroke = 0;
if (pegbar && pegbar->getSpline()) stroke = pegbar->getSpline()->getStroke();
if (!stroke) return;
int frame = TApp::instance()->getCurrentFrame()->getFrame();
TAffine aff;
double objZ = 0, objNoScaleZ = 0;
if (objId != TStageObjectId::NoneId) {
aff = xsh->getParentPlacement(objId, frame);
objZ = xsh->getZ(objId, frame);
objNoScaleZ = xsh->getStageObject(objId)->getGlobalNoScaleZ();
}
glPushMatrix();
if (camera3d) {
tglMultMatrix(aff);
aff = TAffine();
glTranslated(0, 0, objZ);
} else {
TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
double camZ = xsh->getZ(cameraId, frame);
TAffine camAff = xsh->getPlacement(cameraId, frame);
TAffine tmp;
TStageObject::perspective(tmp, camAff, camZ, aff, objZ, objNoScaleZ);
aff = viewMatrix * tmp;
}
if (TApp::instance()->getCurrentObject()->isSpline()) {
glColor3d(1.0, 0.5, 0);
glLineStipple(1, 0x18FF);
} else {
glLineStipple(1, 0xCCCC);
glColor3d(1, 0, 1);
}
glEnable(GL_LINE_STIPPLE);
tglMultMatrix(aff);
double pixelSize = std::max(0.1, pixelsize);
double strokeLength = stroke->getLength();
int n = (int)(5 + (strokeLength / pixelSize) * 0.1);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < n; i++)
tglVertex(stroke->getPoint((double)i / (double)(n - 1)));
glEnd();
glDisable(GL_LINE_STIPPLE);
int cpCount = stroke->getControlPointCount();
for (int i = 0; i * 4 < cpCount; i++) {
double t = stroke->getParameterAtControlPoint(i * 4);
TPointD pos = stroke->getPoint(t);
tglDrawText(pos, QString::number(i).toStdString().c_str());
}
if (pegbar) {
TAffine parentAff = xsh->getParentPlacement(objId, frame);
TAffine aff = xsh->getPlacement(objId, frame);
TPointD center = Stage::inch * xsh->getCenter(objId, frame);
glPushMatrix();
tglMultMatrix(parentAff.inv() * TTranslation(aff * center));
center = TPointD();
// draw center
// tglDrawDisk(center,pixelSize*5);
tglDrawDisk(center, sqrt(tglGetPixelSize2()) * 5);
glPopMatrix();
}
glPopMatrix();
}
//-----------------------------------------------------------------------------
void SceneViewer::resetInputMethod() {
#if QT_VERSION >= 0x050000
QGuiApplication::inputMethod()->reset();
#else
qApp->inputContext()->reset();
#endif
}
//-----------------------------------------------------------------------------
void SceneViewer::set3DLeftSideView() {
m_phi3D = -90;
m_theta3D = 0;
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::set3DRightSideView() {
m_phi3D = 90;
m_theta3D = 0;
invalidateAll();
}
//-----------------------------------------------------------------------------
void SceneViewer::set3DTopView() {
m_phi3D = 0;
m_theta3D = 90;
invalidateAll();
}
//-----------------------------------------------------------------------------
bool SceneViewer::canSwapCompared() const {
return m_visualSettings.m_doCompare && m_previewMode != NO_PREVIEW;
}
//-----------------------------------------------------------------------------
TAffine SceneViewer::getNormalZoomScale() {
return TScale(getDpiFactor()).inv();
}
//-----------------------------------------------------------------------------
void SceneViewer::invalidateToolStatus() {
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
if (tool) {
m_toolDisableReason = tool->updateEnabled();
if (tool->isEnabled()) {
setToolCursor(this, tool->getCursorId());
tool->setViewer(this);
tool->updateMatrix();
} else
setCursor(Qt::ForbiddenCursor);
} else
setCursor(Qt::ForbiddenCursor);
}
//-----------------------------------------------------------------------------
/*! return the viewer geometry in order to avoid picking the style outside of
the viewer
when using the stylepicker and the finger tools
*/
TRectD SceneViewer::getGeometry() const {
int devPixRatio = getDevPixRatio();
TTool *tool = TApp::instance()->getCurrentTool()->getTool();
TPointD topLeft =
tool->getMatrix().inv() * winToWorld(geometry().topLeft() * devPixRatio);
TPointD bottomRight = tool->getMatrix().inv() *
winToWorld(geometry().bottomRight() * devPixRatio);
TObjectHandle *objHandle = TApp::instance()->getCurrentObject();
if (tool->getToolType() & TTool::LevelTool && !objHandle->isSpline()) {
topLeft.x /= m_dpiScale.x;
topLeft.y /= m_dpiScale.y;
bottomRight.x /= m_dpiScale.x;
bottomRight.y /= m_dpiScale.y;
}
return TRectD(topLeft, bottomRight);
}
//-----------------------------------------------------------------------------
/*! delete preview - subcamera executed from context menu
*/
void SceneViewer::doDeleteSubCamera() {
PreviewSubCameraManager::instance()->deleteSubCamera(this);
}
//-----------------------------------------------------------------------------
void SceneViewer::bindFBO() {
if (m_fbo) m_fbo->bind();
}
//-----------------------------------------------------------------------------
void SceneViewer::releaseFBO() {
if (m_fbo) m_fbo->release();
}
//-----------------------------------------------------------------------------
void SceneViewer::onContextAboutToBeDestroyed() {
if (!m_lutCalibrator) return;
makeCurrent();
m_lutCalibrator->cleanup();
doneCurrent();
}
//-----------------------------------------------------------------------------
// called from SceneViewer::initializeGL()
void SceneViewer::registerContext() {
// release the old context, if any
// this will be happen when dock / float the viewer panel.
bool hasOldContext;
#ifdef _WIN32
hasOldContext =
(m_currentContext.first != nullptr && m_currentContext.second != nullptr);
#else
hasOldContext = m_currentContext != nullptr;
#endif
if (hasOldContext) {
int ret = l_contexts.erase(m_currentContext);
if (ret)
TGLDisplayListsManager::instance()->releaseContext(m_currentContext);
}
// then, register context and the space Id correspondent to it.
int displayListId;
if (TApp::instance()->getMainWindow() &&
TApp::instance()->getMainWindow()->isAncestorOf(this) &&
QThread::currentThread() == qGuiApp->thread()) {
// obtain displaySpaceId for main thread
if (l_mainDisplayListsSpaceId == -1)
l_mainDisplayListsSpaceId =
TGLDisplayListsManager::instance()->storeProxy(new DummyProxy);
displayListId = l_mainDisplayListsSpaceId;
}
// for the other cases (such as for floating viewer), it can't share the
// context so
// obtain different id
else
displayListId =
TGLDisplayListsManager::instance()->storeProxy(new DummyProxy);
TGlContext tglContext(tglGetCurrentContext());
TGLDisplayListsManager::instance()->attachContext(displayListId, tglContext);
l_contexts.insert(tglContext);
}