1 /* ***** BEGIN LICENSE BLOCK *****
2  * This file is part of openfx-misc <https://github.com/devernay/openfx-misc>,
3  * Copyright (C) 2013-2018 INRIA
4  *
5  * openfx-misc is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * openfx-misc is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with openfx-misc.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17  * ***** END LICENSE BLOCK ***** */
18 
19 /*
20  * OFX Transform & DirBlur plugins.
21  */
22 
23 #define _USE_MATH_DEFINES
24 #include <cmath> // tan, atan2
25 #include <cstring> // strerror
26 #include <cstdio> // fopen, fclose
27 #include <cstdlib> // atoi, atof
28 #include <cerrno> // errno
29 #include <iostream>
30 
31 #include "ofxsTransform3x3.h"
32 #include "ofxsCoords.h"
33 #include "ofxsThreadSuite.h"
34 #include "ofxsGenerator.h"
35 #include "ofxsFormatResolution.h"
36 #include "ofxsFileOpen.h"
37 
38 using namespace OFX;
39 using std::string;
40 
41 #define kCameraAxis "axis"
42 #define kCameraAxisLabel "Axis"
43 
44 #define kCameraCam "cam"
45 #define kCameraCamLabel "Cam"
46 
47 #define kGroupCard "card"
48 #define kGroupCardLabel "Card"
49 
50 
51 //#define kParamTransformAmount "transformAmount"
52 //#define kParamTransformAmountLabel "Amount"
53 //#define kParamTransformAmountHint "Amount of transform to apply (excluding the extra matrix, which is always applied). 0 means the transform is identity, 1 means to apply the full transform."
54 
55 #define kParamTransformInteractive "interactive"
56 #define kParamTransformInteractiveLabel "Interactive Update"
57 #define kParamTransformInteractiveHint "If checked, update the parameter values during interaction with the image viewer, else update the values when pen is released."
58 
59 OFXS_NAMESPACE_ANONYMOUS_ENTER
60 
61 #define kPluginName "Card3DOFX"
62 #define kPluginGrouping "Transform"
63 #define kPluginDescription "Card3D.\n" \
64     "This effect applies a transform that corresponds to projection the source image onto a 3D card in space. The 3D card is positionned with relative to the Axis position, and the Camera position may also be given. The Axis may be used to apply the same global motion to several cards.\n" \
65     "This plugin concatenates transforms.\n" \
66     "http://opticalenquiry.com/nuke/index.php?title=Card3D"
67 
68 #define kPluginIdentifier "net.sf.openfx.Card3D"
69 #define kPluginVersionMajor 1 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
70 #define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.
71 
72 #define kParamSrcClipChanged "srcClipChanged"
73 
74 #define kParamCamEnable "camEnable"
75 #define kParamCamEnableLabel "Enable Camera", "Enable the camera projection parameters."
76 
77 #define kParamLensInFocal "lensInFocal"
78 #define kParamLensInFocalLabel "Lens-In Focal", "The focal length of the camera that took the picture on the card. The card is scaled so that at distance 1 (which is the default card Z) it occupies the field of view corresponding to lensInFocal and lensInHAperture."
79 
80 #define kParamLensInHAperture "lensInHAperture"
81 #define kParamLensInHApertureLabel "Lens-In H.Aperture", "The horizontal aperture (or sensor/film back width) of the camera that took the picture on the card. The card is scaled so that at distance 1 (which is the default card Z) it occupies the field of view corresponding to lensInFocal and lensInHAperture."
82 
83 #define kParamOutputFormat "format"
84 #define kParamOutputFormatLabel "Output Format", "Desired format for the output sequence."
85 
86 // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
87 #define kParamDefaultsNormalised "defaultsNormalised"
88 
89 static bool gHostSupportsDefaultCoordinateSystem = true; // for kParamDefaultsNormalised
90 
91 ////////////////////////////////////////////////////////////////////////////////
92 // BEGIN CameraParm
93 
94 // Camera Projection parameters
95 
96 
97 #define kParamCameraProjectionGroup "Projection"
98 #define kParamCameraProjectionGroupLabel "Projection"
99 
100 #define kParamCameraProjectionMode kNukeOfxCameraParamProjectionMode
101 #define kParamCameraProjectionModeLabel "Projection"
102 #define kParamCameraProjectionModeOptionPerspective "Perspective", "Perspective projection.", "perspective"
103 #define kParamCameraProjectionModeOptionOrthographic "Orthographic", "Orthographic projection", "orthographic"
104 //#define kParamCameraProjectionModeOptionUV "UV", "UV projection", "uv" // used in Nuke to compute uv coordinates instead of pixel values
105 //#define kParamCameraProjectionModeOptionSpherical "Spherical", "Spherical projection." , "spherical"
106 enum CameraProjectionModeEnum {
107     eCameraProjectionModePerspective = 0,
108     eCameraProjectionModeOrthographic,
109     eCameraProjectionModeUV,
110     eCameraProjectionModeSpherical,
111 };
112 /* other camera projections from nona http://hugin.sourceforge.net/docs/nona/nona.txt
113 see also http://wiki.panotools.org/Projections http://wiki.panotools.org/Fisheye
114 #                  0 - rectilinear (for printing and viewing) [R = f.tan(theta)] http://wiki.panotools.org/Rectilinear_Projection
115 #                  1 - Cylindrical (for Printing and QTVR) http://wiki.panotools.org/Cylindrical_Projection
116 #                  2 - Equirectangular ( for Spherical panos), default http://wiki.panotools.org/Equirectangular_Projection
117 #                  3 - full-frame fisheye [R = f.theta], e.g. Peleng 8mm f/3.5 Fisheye
118 #                  4 - Stereographic [R = 2f.tan(theta/2)], e.g. Samyang 8 mm f/3.5
119 #                  5 - Mercator http://wiki.panotools.org/Projections#Mercator_projection
120 #                  6 - Transverse Mercator
121 #                  7 - Sinusoidal http://wiki.panotools.org/Projections#Sinusoidal_projection
122 #                  8 - Lambert Cylindrical Equal Area
123 #                  9 - Lambert Equal Area Azimuthal
124 #                 10 - Albers Equal Area Conic
125 #                 11 - Miller Cylindrical http://wiki.panotools.org/Projections#Miller_projection
126 #                 12 - Panini (obsolete non-Panini projection)
127 #                 13 - Architectural (Miller above the horizon, Lambert Equal Area below
128 #                 14 - Orthographic [R = f.sin(theta)], e.g. Yasuhara - MADOKA 180 circle fisheye lens
129 #                 15 - Equisolid (equal-area fisheye) [R=2f.sin(theta/2)], e. g. Sigma 8mm f/4.0 AF EX, Olympus 8mm F2.8, Nikon 6mm F2.8, Nikon 8mm F2.8 (also convex mirror)
130 #                 16 - Equirectangular Panini (standard Panini, covered by Panini General)
131 #                 17 - Biplane
132 #                 18 - Triplane
133 #                 19 - Panini General http://wiki.panotools.org/The_General_Panini_Projection has 3 parameters:  http://wiki.panotools.org/The_General_Panini_Projection#Parameters
134 #                 20 - Thoby Projection [R = k1.f.sin(k2.theta) k1=1.47 k2=0.713], e.g. AF DX Fisheye-Nikkor 10.5mm f/2.8G ED
135 #                 21 - Hammer-Aitoff Projection
136 
137 lens may be:
138 #                  0 - rectilinear (normal lenses)
139 #                  1 - Panoramic (Scanning cameras like Noblex)
140 #                  2 - Circular fisheye
141 #                  3 - full-frame fisheye
142 #                  4 - PSphere, equirectangular
143 #                  8 - orthographic fisheye
144 #                 10 - stereographic fisheye
145 #                 21 - Equisolid fisheye
146 #                 20 - Fisheye Thoby (Nikkor 10.5)
147 
148 lens supported by videostitch studio:
149     2 - Equirectangular
150     4 - Stereographic
151     0 - Rectilinear
152     ??? Circular fisheye
153     3 - Full-frame fisheye
154 
155  */
156 #define kParamCameraFocalLength kNukeOfxCameraParamFocalLength
157 #define kParamCameraFocalLengthLabel "Focal Length", "The camera focal length, in arbitrary units (usually either millimeters or 35 mm equivalent focal length). haperture and vaperture must be expressed in the same units."
158 #define kParamCameraHorizontalAperture kNukeOfxCameraParamHorizontalAperture
159 #define kParamCameraHorizontalApertureLabel "Horiz. Aperture", "The camera horizontal aperture (or film back width), in the same units as the focal length. In the case of scanned film, this can be obtained as image_width * scanner_pitch."
160 #define kParamCameraVerticalAperture kNukeOfxCameraParamVerticalAperture
161 #define kParamCameraVerticalApertureLabel "Vert. Aperture", "The camera vertical aperture (or film back height), in the same units as the focal length. This does not affect the projection (which is computed from haperture and the image aspect ratio), but it is used to compute the focal length from vertical FOV when importing chan files, using the formula: focal = 0.5 * vaperture / tan(vfov/2). It is thus best set as: haperture = vaperture * image_width/image_height. In the case of scanned film, this can be obtained as image_height * scanner_pitch."
162 #define kParamCameraNear kNukeOfxCameraParamNear
163 #define kParamCameraNearLabel "Near"
164 #define kParamCameraFar kNukeOfxCameraParamFar
165 #define kParamCameraFarLabel "Far"
166 #define kParamCameraWindowTranslate kNukeOfxCameraParamWindowTranslate
167 #define kParamCameraWindowTranslateLabel "Window Translate", "The camera window (or film back) is translated by this fraction of the horizontal aperture, without changing the position of the camera center. This can be used to model tilt-shift or perspective-control lens."
168 #define kParamCameraWindowScale kNukeOfxCameraParamWindowScale
169 #define kParamCameraWindowScaleLabel "Window Scale", "Scale the camera window (or film back)."
170 #define kParamCameraWindowRoll kNukeOfxCameraParamWindowRoll
171 #define kParamCameraWindowRollLabel "Window Roll", "Rotation (in degrees) of the camera window (or film back) around the z axis."
172 #define kParamCameraFocalPoint kNukeOfxCameraParamFocalPoint
173 #define kParamCameraFocalPointLabel "Focus Distance", "Focus distance of the camera (used for depth-of-field effects)."
174 #define kParamCameraFStop "fstop"
175 #define kParamCameraFStopLabel "F-Stop", "F-stop (relative aperture) of the camera (used for depth-of-field effects)."
176 
177 class CameraParam {
178     friend class PosMatParam;
179 
180     std::string _prefix;
181     ChoiceParam* _camProjectionMode;
182     DoubleParam* _camFocalLength;
183     DoubleParam* _camHAperture;
184     DoubleParam* _camVAperture;
185     DoubleParam* _camNear;
186     DoubleParam* _camFar;
187     Double2DParam* _camWinTranslate;
188     Double2DParam* _camWinScale;
189     DoubleParam* _camWinRoll;
190     DoubleParam* _camFocusDistance;
191     DoubleParam* _camFStop;
192     bool _enabled;
193 
194 public:
CameraParam(OFX::ImageEffect * parent,const std::string & prefix)195     CameraParam(OFX::ImageEffect* parent, const std::string& prefix)
196     : _prefix(prefix)
197     , _camProjectionMode(NULL)
198     , _camFocalLength(NULL)
199     , _camHAperture(NULL)
200     , _camVAperture(NULL)
201     , _camNear(NULL)
202     , _camFar(NULL)
203     , _camWinTranslate(NULL)
204     , _camWinScale(NULL)
205     , _camWinRoll(NULL)
206     , _camFocusDistance(NULL)
207     , _camFStop(NULL)
208     , _enabled(true)
209     {
210         _camProjectionMode = parent->fetchChoiceParam(prefix + kParamCameraProjectionMode);
211         _camFocalLength = parent->fetchDoubleParam(prefix + kParamCameraFocalLength);
212         _camHAperture = parent->fetchDoubleParam(prefix + kParamCameraHorizontalAperture);
213         if (parent->paramExists(prefix + kParamCameraVerticalAperture)) {
214             _camVAperture = parent->fetchDoubleParam(prefix + kParamCameraVerticalAperture);
215         }
216         if (parent->paramExists(prefix + kParamCameraNear)) {
217             _camNear = parent->fetchDoubleParam(prefix + kParamCameraNear);
218         }
219         if (parent->paramExists(prefix + kParamCameraFar)) {
220             _camFar = parent->fetchDoubleParam(prefix + kParamCameraFar);
221         }
222         _camWinTranslate = parent->fetchDouble2DParam(prefix + kParamCameraWindowTranslate);
223         _camWinScale = parent->fetchDouble2DParam(prefix + kParamCameraWindowScale);
224         _camWinRoll = parent->fetchDoubleParam(prefix + kParamCameraWindowRoll);
225         if (parent->paramExists(prefix + kParamCameraFocalPoint)) {
226             _camFocusDistance = parent->fetchDoubleParam(prefix + kParamCameraFocalPoint);
227         }
228         if (parent->paramExists(prefix + kParamCameraFStop)) {
229             _camFStop = parent->fetchDoubleParam(prefix + kParamCameraFStop);
230         }
231     }
232 
233     void getValueAtTime(double time,
234                         CameraProjectionModeEnum& projectionMode,
235                         double& focalLength,
236                         double& hAperture,
237                         double& winTranslateU,
238                         double& winTranslateV,
239                         double& winScaleU,
240                         double& winScaleV,
241                         double& winRoll) const;
242 
243     static void getMatrix(const Matrix4x4& pos,
244                           CameraProjectionModeEnum projectionMode,
245                           double focalLength,
246                           double hAperture,
247                           double winTranslateU,
248                           double winTranslateV,
249                           double winScaleU,
250                           double winScaleV,
251                           double winRoll,
252                           Matrix3x3* mat);
253 
254     static void define(ImageEffectDescriptor &desc,
255                        PageParamDescriptor *page,
256                        GroupParamDescriptor *group,
257                        const std::string prefix);
258 
setEnabled(bool enabled)259     void setEnabled(bool enabled) { _enabled = enabled; update(); }
260 
261 private:
262 
update()263     void update()
264     {
265         _camProjectionMode->setEnabled(_enabled);
266         _camFocalLength->setEnabled(_enabled);
267         _camHAperture->setEnabled(_enabled);
268         if (_camVAperture) {
269             _camVAperture->setEnabled(_enabled);
270         }
271         if (_camNear) {
272             _camNear->setEnabled(_enabled);
273         }
274         if (_camFar) {
275             _camFar->setEnabled(_enabled);
276         }
277         _camWinTranslate->setEnabled(_enabled);
278         _camWinScale->setEnabled(_enabled);
279         _camWinRoll->setEnabled(_enabled);
280         if (_camFocusDistance) {
281             _camFocusDistance->setEnabled(_enabled);
282         }
283         if (_camFStop) {
284             _camFStop->setEnabled(_enabled);
285         }
286     }
287 };
288 
289 
290 
291 void
define(ImageEffectDescriptor & desc,PageParamDescriptor * page,GroupParamDescriptor * group,const std::string prefix)292 CameraParam::define(ImageEffectDescriptor &desc,
293                     PageParamDescriptor *page,
294                     GroupParamDescriptor *group,
295                     const std::string prefix)
296 {
297     {
298         ChoiceParamDescriptor* param = desc.defineChoiceParam(prefix + kParamCameraProjectionMode);
299         param->setLabel(kParamCameraProjectionModeLabel);
300         assert(param->getNOptions() == eCameraProjectionModePerspective);
301         param->appendOption(kParamCameraProjectionModeOptionPerspective);
302         assert(param->getNOptions() == eCameraProjectionModeOrthographic);
303         param->appendOption(kParamCameraProjectionModeOptionOrthographic);
304         /*
305         assert(param->getNOptions() == eCameraProjectionModeUV);
306         param->appendOption(kParamCameraProjectionModeOptionUV);
307         assert(param->getNOptions() == eCameraProjectionModeSpherical);
308         param->appendOption(kParamCameraProjectionModeOptionSpherical);
309         //*/
310         param->setDefault(eCameraProjectionModePerspective);
311         param->setAnimates(false);
312         if (group) {
313             param->setParent(*group);
314         }
315         if (page) {
316             page->addChild(*param);
317         }
318     }
319     {
320         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraFocalLength);
321         param->setLabelAndHint(kParamCameraFocalLengthLabel);
322         param->setRange(1e-8, DBL_MAX);
323         param->setDisplayRange(5, 100);
324         param->setDefault(50.);
325         if (group) {
326             param->setParent(*group);
327         }
328         if (page) {
329             page->addChild(*param);
330         }
331     }
332     {
333         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraHorizontalAperture);
334         param->setLabelAndHint(kParamCameraHorizontalApertureLabel);
335         param->setRange(1e-8, DBL_MAX);
336         param->setDisplayRange(0.1, 50);
337         param->setDefault(24.576);
338         if (group) {
339             param->setParent(*group);
340         }
341         if (page) {
342             page->addChild(*param);
343         }
344     }
345     {
346         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraVerticalAperture);
347         param->setLabelAndHint(kParamCameraVerticalApertureLabel);
348         param->setRange(1e-8, DBL_MAX);
349         param->setDisplayRange(0.1, 50);
350         param->setDefault(18.672);
351         if (group) {
352             param->setParent(*group);
353         }
354         if (page) {
355             page->addChild(*param);
356         }
357     }
358     /*
359     {
360         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraNear);
361         param->setLabelAndHint(kParamCameraNearLabel);
362         param->setRange(1e-8, DBL_MAX);
363         param->setDisplayRange(0.1, 10);
364         param->setDefault(0.1);
365         if (group) {
366             param->setParent(*group);
367         }
368         if (page) {
369             page->addChild(*param);
370         }
371     }
372     {
373         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraFar);
374         param->setLabelAndHint(kParamCameraFarLabel);
375         param->setRange(1e-8, DBL_MAX);
376         param->setDisplayRange(11, 10000);
377         param->setDefault(10000);
378         if (group) {
379             param->setParent(*group);
380         }
381         if (page) {
382             page->addChild(*param);
383         }
384     }
385     //*/
386     {
387         Double2DParamDescriptor* param = desc.defineDouble2DParam(prefix + kParamCameraWindowTranslate);
388         param->setLabelAndHint(kParamCameraWindowTranslateLabel);
389         param->setRange(-1, -1, 1, 1);
390         param->setDisplayRange(-1, -1, 1, 1);
391         param->setDoubleType(eDoubleTypePlain);
392         if (group) {
393             param->setParent(*group);
394         }
395         if (page) {
396             page->addChild(*param);
397         }
398     }
399     {
400         Double2DParamDescriptor* param = desc.defineDouble2DParam(prefix + kParamCameraWindowScale);
401         param->setLabelAndHint(kParamCameraWindowScaleLabel);
402         param->setRange(1e-8, 1e-8, DBL_MAX, DBL_MAX);
403         param->setDisplayRange(0.1, 0.1, 10, 10);
404         param->setDefault(1, 1);
405         param->setDoubleType(eDoubleTypeScale);
406         if (group) {
407             param->setParent(*group);
408         }
409         if (page) {
410             page->addChild(*param);
411         }
412     }
413     {
414         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraWindowRoll);
415         param->setLabelAndHint(kParamCameraWindowRollLabel);
416         param->setRange(-DBL_MAX, DBL_MAX);
417         param->setDisplayRange(-45, 45);
418         param->setDoubleType(eDoubleTypeAngle);
419         if (group) {
420             param->setParent(*group);
421         }
422         if (page) {
423             page->addChild(*param);
424         }
425     }
426     /*
427     {
428         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraFocalPoint);
429         param->setLabelAndHint(kParamCameraFocalPointLabel);
430         param->setRange(1e-8, DBL_MAX);
431         param->setDisplayRange(0.1, 10);
432         param->setDefault(2);
433         if (group) {
434             param->setParent(*group);
435         }
436         if (page) {
437             page->addChild(*subgroup);
438         }
439     }
440     {
441         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamCameraFStop);
442         param->setLabelAndHint(kParamCameraFStopLabel);
443         param->setRange(1e-8, DBL_MAX);
444         param->setDisplayRange(0.1, 30);
445         param->setDefault(16);
446         if (group) {
447             param->setParent(*group);
448         }
449         if (page) {
450             page->addChild(*param);
451         }
452     }
453     //*/
454 }
455 
456 void
getValueAtTime(double time,CameraProjectionModeEnum & projectionMode,double & focalLength,double & hAperture,double & winTranslateU,double & winTranslateV,double & winScaleU,double & winScaleV,double & winRoll) const457 CameraParam::getValueAtTime(double time,
458                             CameraProjectionModeEnum& projectionMode,
459                             double& focalLength,
460                             double& hAperture,
461                             double& winTranslateU,
462                             double& winTranslateV,
463                             double& winScaleU,
464                             double& winScaleV,
465                             double& winRoll) const
466 {
467     projectionMode = (CameraProjectionModeEnum)_camProjectionMode->getValueAtTime(time);
468     focalLength = _camFocalLength->getValueAtTime(time);
469     hAperture = _camHAperture->getValueAtTime(time);
470     _camWinTranslate->getValueAtTime(time, winTranslateU, winTranslateV);
471     _camWinScale->getValueAtTime(time, winScaleU, winScaleV);
472     winRoll = _camWinRoll->getValueAtTime(time);
473 }
474 
475 void
getMatrix(const Matrix4x4 & pos,const CameraProjectionModeEnum projectionMode,const double focalLength,const double hAperture,const double winTranslateU,const double winTranslateV,const double winScaleU,const double winScaleV,const double winRoll,Matrix3x3 * mat)476 CameraParam::getMatrix(const Matrix4x4& pos,
477                        const CameraProjectionModeEnum projectionMode,
478                        const double focalLength,
479                        const double hAperture,
480                        const double winTranslateU,
481                        const double winTranslateV,
482                        const double winScaleU,
483                        const double winScaleV,
484                        const double winRoll,
485                        Matrix3x3* mat)
486 {
487     // apply camera params
488     double a = hAperture / std::max(1e-8, focalLength);
489 #pragma message WARN("make sure the standard camera matrix gives z > 0 in orthographic and projective cases")
490     (*mat)(0,0) = -pos(0,0); (*mat)(0,1) = -pos(0,1); (*mat)(0,2) = -pos(0,3);
491     (*mat)(1,0) = -pos(1,0); (*mat)(1,1) = -pos(1,1); (*mat)(1,2) = -pos(1,3);
492     if (projectionMode == eCameraProjectionModePerspective) {
493         // divide by Z
494         (*mat)(2,0) = -a * pos(2,0); (*mat)(2,1) = -a * pos(2,1); (*mat)(2,2) = -a * pos(2,3);
495     } else {
496         // orthographic
497         (*mat)(2,0) = a * pos(3,0); (*mat)(2,1) = a * pos(3,1); (*mat)(2,2) = a * pos(3,3);
498     }
499     // apply winTranslate
500     (*mat)(0,0) += (*mat)(2,0) * winTranslateU / 2.;
501     (*mat)(1,0) += (*mat)(2,0) * winTranslateV / 2.;
502     (*mat)(0,1) += (*mat)(2,1) * winTranslateU / 2.;
503     (*mat)(1,1) += (*mat)(2,1) * winTranslateV / 2.;
504     (*mat)(0,2) += (*mat)(2,2) * winTranslateU / 2.;
505     (*mat)(1,2) += (*mat)(2,2) * winTranslateV / 2.;
506     // apply winScale
507     (*mat)(0,0) /= winScaleU;
508     (*mat)(0,1) /= winScaleU;
509     (*mat)(0,2) /= winScaleU;
510     (*mat)(1,0) /= winScaleV;
511     (*mat)(1,1) /= winScaleV;
512     (*mat)(1,2) /= winScaleV;
513     // apply winRoll
514     if (winRoll != 0.) {
515         double s = std::sin(winRoll * M_PI / 180.);
516         double c = std::cos(winRoll * M_PI / 180.);
517         *mat = Matrix3x3(c, -s, 0,
518                          s, c, 0,
519                          0, 0, 1) * *mat;
520     }
521 
522 }
523 
524 // END CameraParm
525 ////////////////////////////////////////////////////////////////////////////////
526 
527 ////////////////////////////////////////////////////////////////////////////////
528 // BEGIN PosMatParm
529 
530 enum PosMatTypeEnum {
531     ePosMatAxis = 0,
532     ePosMatCamera,
533     ePosMatCard,
534 };
535 
536 #define kParamPosMatFile "File"
537 #define kParamPosMatFileLabel "File", "Import/export data"
538 
539 #define kParamPosMatImportFile "ImportFile"
540 #define kParamPosMatImportFileLabel "Import", "Import a chan file created using 3D tracking software, or a txt file created using Boujou."
541 #define kParamPosMatImportFileReload "ImportFileReload"
542 #define kParamPosMatImportFileReloadLabel "Reload", "Reload the file."
543 
544 #define kParamPosMatImportFormat "ImportFormat"
545 #define kParamPosMatImportFormatLabel "Import Format", "The format of the file to import."
546 #define kParamPosMatImportFormatOptionChan "chan", "Chan format, each line is FRAME TX TY TZ RX RY RZ VFOV. Can be created using Natron, Nuke, 3D-Equalizer, Maya and other 3D tracking software. Be careful that the rotation order must be exactly the same when exporting and importing the chan file.", "chan"
547 #define kParamPosMatImportFormatOptionBoujou "Boujou", "Boujou text export. In Boujou, after finishing the track and solving, go to Export > Export Camera Solve (Or press F12) > choose where to save the data and give it a name, click he drop down Export Type and make sure it will save as a .txt, then click Save. Each camera line is R(0,0) R(0,1) R(0,2) R(1,0) R(1,1) R(1,2) R(2,0) R(2,1) R(2,2) Tx Ty Tz F(mm).", "boujou"
548 enum ImportFormatEnum {
549     eImportFormatChan = 0,
550     eImportFormatBoujou,
551 };
552 
553 #define kParamPosMatExportChan "ExportChan"
554 #define kParamPosMatExportChanLabel "Export", "Export a .chan file which can be used in Natron, Nuke or 3D tracking software, such as 3D-Equalizer, Maya, or Boujou. Be careful that the rotation order must be exactly the same when exporting and importing the chan file."
555 
556 #define kParamPosMatExportChanRewrite "ExportChanRewrite"
557 #define kParamPosMatExportChanRewriteLabel "Rewrite", "Rewrite the .chan file."
558 
559 #define kParamPosMatTransformOrder "XformOrder"
560 #define kParamPosMatTransformOrderLabel "Transform Order", "Order in which scale (S), rotation (R) and translation (T) are applied."
561 #define kParamPosMatTransformOrderOptionSRT "SRT", "Scale, Rotation, Translation.", "srt"
562 #define kParamPosMatTransformOrderOptionSTR "STR", "Scale, Translation, Rotation.", "str"
563 #define kParamPosMatTransformOrderOptionRST "RST", "Rotation, Scale, Translation.", "rst"
564 #define kParamPosMatTransformOrderOptionRTS "RTS", "Rotation, Translation, Scale.", "rts"
565 #define kParamPosMatTransformOrderOptionTSR "TSR", "Translation, Scale, Rotation.", "tsr"
566 #define kParamPosMatTransformOrderOptionTRS "TRS", "Translation, Rotation, Scale.", "trs"
567 enum PosMatTransformOrderEnum {
568     ePosMatTransformOrderSRT = 0,
569     ePosMatTransformOrderSTR,
570     ePosMatTransformOrderRST,
571     ePosMatTransformOrderRTS,
572     ePosMatTransformOrderTSR,
573     ePosMatTransformOrderTRS,
574 };
575 #define kParamPosMatTransformOrderDefault ePosMatTransformOrderSRT
576 
577 #define kParamPosMatRotationOrder "RotOrder"
578 #define kParamPosMatRotationOrderLabel "Rotation Order", "Order in which Euler angles are applied in the rotation."
579 #define kParamPosMatRotationOrderOptionXYZ "XYZ", "Rotation over X axis, then Y and Z.", "xyz"
580 #define kParamPosMatRotationOrderOptionXZY "XZY", "Rotation over X axis, then Z and Y.", "xzy"
581 #define kParamPosMatRotationOrderOptionYXZ "YXZ", "Rotation over Y axis, then X and Z.", "yxz"
582 #define kParamPosMatRotationOrderOptionYZX "YZX", "Rotation over Y axis, then Z and X.", "yzx"
583 #define kParamPosMatRotationOrderOptionZXY "ZXY", "Rotation over Z axis, then X and Y.", "zxy"
584 #define kParamPosMatRotationOrderOptionZYX "ZYX", "Rotation over Z axis, then Y and X.", "zyx"
585 enum PosMatRotationOrderEnum {
586     ePosMatRotationOrderXYZ = 0,
587     ePosMatRotationOrderXZY,
588     ePosMatRotationOrderYXZ,
589     ePosMatRotationOrderYZX,
590     ePosMatRotationOrderZXY,
591     ePosMatRotationOrderZYX,
592 };
593 #define kParamPosMatRotationOrderDefault ePosMatRotationOrderZXY
594 
595 #define kParamPosMatTranslate "Translate"
596 #define kParamPosMatTranslateLabel "Translate", "Translation component."
597 
598 #define kParamPosMatRotate "Rotate"
599 #define kParamPosMatRotateLabel "Rotate", "Euler angles (in degrees)."
600 
601 #define kParamPosMatScale "Scaling"
602 #define kParamPosMatScaleLabel "Scale", "Scale factor over each axis."
603 
604 #define kParamPosMatUniformScale "UniformScale"
605 #define kParamPosMatUniformScaleLabel "Uniform Scale", "Scale factor over all axis. It is multiplied by the scale factor over each axis."
606 
607 #define kParamPosMatSkew "Skew"
608 #define kParamPosMatSkewLabel "Skew", "Skew over each axis, in degrees."
609 
610 #define kParamPosMatPivot "Pivot"
611 #define kParamPosMatPivotLabel "Pivot", "The position of the origin for position, scaling, skewing, and rotation."
612 
613 #define kGroupPosMatLocalMatrix "LocalMatrix"
614 #define kGroupPosMatLocalMatrixLabel "Local Matrix"
615 
616 #define kParamPosMatUseMatrix "UseMatrix"
617 #define kParamPosMatUseMatrixLabel "Specify Matrix", "Check to specify manually all the values for the position matrix."
618 
619 #define kParamPosMatMatrix "Matrix"
620 #define kParamPosMatMatrixLabel "", "Matrix coefficient."
621 
622 
623 class PosMatParam {
624     ImageEffect* _effect;
625     Clip* _srcClip;
626     std::string _prefix;
627     GroupParam* _fileGroup;
628     StringParam* _importFile;
629     PushButtonParam* _importFileReload;
630     ChoiceParam* _importFormat;
631     StringParam* _exportChan;
632     PushButtonParam* _exportChanRewrite;
633     ChoiceParam* _transformOrder;
634     ChoiceParam* _rotationOrder;
635     Double3DParam* _translate;
636     Double3DParam* _rotate;
637     Double3DParam* _scale;
638     DoubleParam* _uniformScale;
639     Double3DParam* _skew;
640     Double3DParam* _pivot;
641     GroupParam* _localMatrix;
642     BooleanParam* _useMatrix;
643     DoubleParam* _matrix[4][4];
644     GroupParam* _projectionGroup;
645     CameraParam* _projection;
646     bool _enabled;
647     PosMatTypeEnum _type;
648 
649 public:
PosMatParam(OFX::ImageEffect * parent,const std::string & prefix,PosMatTypeEnum type)650     PosMatParam(OFX::ImageEffect* parent, const std::string& prefix, PosMatTypeEnum type)
651     : _effect(parent)
652     , _srcClip(NULL)
653     , _prefix(prefix)
654     , _fileGroup(NULL)
655     , _importFile(NULL)
656     , _importFileReload(NULL)
657     , _importFormat(NULL)
658     , _exportChan(NULL)
659     , _exportChanRewrite(NULL)
660     , _transformOrder(NULL)
661     , _rotationOrder(NULL)
662     , _translate(NULL)
663     , _rotate(NULL)
664     , _scale(NULL)
665     , _uniformScale(NULL)
666     , _skew(NULL)
667     , _pivot(NULL)
668     , _localMatrix(NULL)
669     , _useMatrix(NULL)
670     , _projectionGroup(NULL)
671     , _projection(NULL)
672     , _enabled(true)
673     , _type(type)
674     {
675         _srcClip = _effect->fetchClip(kOfxImageEffectSimpleSourceClipName);
676         _fileGroup = _effect->fetchGroupParam(prefix + kParamPosMatFile);
677         _importFile = _effect->fetchStringParam(prefix + kParamPosMatImportFile);
678         if (_effect->paramExists(prefix + kParamPosMatImportFileReload)) {
679             _importFileReload = _effect->fetchPushButtonParam(prefix + kParamPosMatImportFileReload);
680         }
681         _importFormat = _effect->fetchChoiceParam(prefix + kParamPosMatImportFormat);
682         _exportChan = _effect->fetchStringParam(prefix + kParamPosMatExportChan);
683         if (_effect->paramExists(prefix + kParamPosMatExportChanRewrite)) {
684             _exportChanRewrite = _effect->fetchPushButtonParam(prefix + kParamPosMatExportChanRewrite);
685         }
686         _transformOrder = _effect->fetchChoiceParam(prefix + kParamPosMatTransformOrder);
687         _rotationOrder = _effect->fetchChoiceParam(prefix + kParamPosMatRotationOrder);
688         _translate = _effect->fetchDouble3DParam(prefix + kParamPosMatTranslate);
689         _rotate = _effect->fetchDouble3DParam(prefix + kParamPosMatRotate);
690         _scale = _effect->fetchDouble3DParam(prefix + kParamPosMatScale);
691         _uniformScale = _effect->fetchDoubleParam(prefix + kParamPosMatUniformScale);
692         _skew = _effect->fetchDouble3DParam(prefix + kParamPosMatSkew);
693         _pivot = _effect->fetchDouble3DParam(prefix + kParamPosMatPivot);
694         _localMatrix = _effect->fetchGroupParam(prefix + kGroupPosMatLocalMatrix);
695         _useMatrix = _effect->fetchBooleanParam(prefix + kParamPosMatUseMatrix);
696         for (int i = 0; i < 4; ++i) {
697             for (int j = 0; j < 4; ++j) {
698                 _matrix[i][j] = _effect->fetchDoubleParam(prefix + kParamPosMatMatrix + (char)('1' + i) + (char)('1' + j));
699             }
700         }
701         if (_type == ePosMatCamera) {
702             _projectionGroup = _effect->fetchGroupParam(kCameraCam kParamCameraProjectionGroup);
703             _projection = new CameraParam(_effect, kCameraCam);
704         }
705         update();
706     }
707 
~PosMatParam()708     virtual ~PosMatParam()
709     {
710         delete _projection;
711     }
712 
713     void changedParam(const InstanceChangedArgs &args, const std::string &paramName);
714 
715     void getMatrix(double time, Matrix4x4* mat) const;
716 
717     static void define(ImageEffectDescriptor &desc,
718                        PageParamDescriptor *page,
719                        GroupParamDescriptor *group,
720                        const std::string prefix,
721                        PosMatTypeEnum type);
722 
setEnabled(bool enabled)723     void setEnabled(bool enabled) { _enabled = enabled; update(); }
724 
getProjection()725     const CameraParam& getProjection() { assert(_projection); return *_projection; }
726 
727 private:
728 
729     /// update visibility/enabledness
update()730     void update()
731     {
732         bool useMatrix = _useMatrix->getValue();
733         _fileGroup->setEnabled(_enabled && !useMatrix);
734         _importFile->setEnabled(_enabled && !useMatrix);
735         if (_importFileReload) {
736             _importFileReload->setEnabled(_enabled && !useMatrix);
737         }
738         _exportChan->setEnabled(_enabled && !useMatrix);
739         if (_exportChanRewrite) {
740             _exportChanRewrite->setEnabled(_enabled && !useMatrix);
741         }
742         _transformOrder->setEnabled(_enabled && !useMatrix);
743         _rotationOrder->setEnabled(_enabled && !useMatrix);
744         _translate->setEnabled(_enabled && !useMatrix);
745         _rotate->setEnabled(_enabled && !useMatrix);
746         _scale->setEnabled(_enabled && !useMatrix);
747         _uniformScale->setEnabled(_enabled && !useMatrix);
748         _skew->setEnabled(_enabled && !useMatrix);
749         _pivot->setEnabled(_enabled && !useMatrix);
750         _localMatrix->setEnabled(_enabled);
751         _useMatrix->setEnabled(_enabled);
752         for (int i = 0; i < 4; ++i) {
753             for (int j = 0; j < 4; ++j) {
754                 _matrix[i][j]->setEnabled(_enabled && useMatrix);
755             }
756         }
757         if (_projectionGroup) {
758             _projectionGroup->setEnabled(_enabled);
759         }
760         if (_projection) {
761             _projection->setEnabled(_enabled);
762         }
763     }
764 
765     void importChan();
766 
767     void importBoujou();
768 
769     void exportChan();
770 
771     struct ChanLine {
772         int frame;
773         double tx, ty, tz, rx, ry, rz, vfov;
774 
ChanLinePosMatParam::ChanLine775         ChanLine()
776         : frame(-1)
777         , tx(0)
778         , ty(0)
779         , tz(0)
780         , rx(0)
781         , ry(0)
782         , rz(0)
783         , vfov(0)
784         {
785         }
786     };
787 };
788 
789 static std::string
trim(std::string const & str)790 trim(std::string const & str)
791 {
792     const std::string whitespace = " \t\f\v\n\r";
793     std::size_t first = str.find_first_not_of(whitespace);
794 
795     // If there is no non-whitespace character, both first and last will be std::string::npos (-1)
796     // There is no point in checking both, since if either doesn't work, the
797     // other won't work, either.
798     if (first == std::string::npos) {
799         return "";
800     }
801 
802     std::size_t last  = str.find_last_not_of(whitespace);
803 
804     return str.substr(first, last - first + 1);
805 }
806 
807 void
importChan()808 PosMatParam::importChan()
809 {
810     string filename;
811     _importFile->getValue(filename);
812     if ( filename.empty() ) {
813         // no filename, do nothing
814         return;
815     }
816     FILE* f = fopen_utf8(filename.c_str(), "r");
817     if (!f) {
818         _effect->sendMessage(Message::eMessageError, "", "Cannot read " + filename + ": " + std::strerror(errno), false);
819 
820         return;
821     }
822     std::list<ChanLine> lines;
823     char buf[1024];
824 
825     while (std::fgets(buf, sizeof buf, f) != NULL) {
826         const string bufstr( trim(buf) );
827         if (bufstr.size() > 0 && bufstr[0] != '#') {
828             const char* b = bufstr.c_str();
829             ChanLine l;
830             bool err = false;
831             if (_type == ePosMatCamera) {
832                 int ret = std::sscanf(b, "%d%lf%lf%lf%lf%lf%lf%lf",
833                                       &l.frame, &l.tx, &l.ty, &l.tz, &l.rx, &l.ry, &l.rz, &l.vfov);
834                 if (ret == 8) {
835                     lines.push_back(l);
836                 } else {
837                     err = true;
838                 }
839             } else {
840                 int ret = std::sscanf(b, "%d%lf%lf%lf%lf%lf%lf",
841                                       &l.frame, &l.tx, &l.ty, &l.tz, &l.rx, &l.ry, &l.rz);
842                 if (ret == 7) {
843                     lines.push_back(l);
844                 } else {
845                     err = true;
846                 }
847             }
848             if (err) {
849                 std::fclose(f);
850                 _effect->sendMessage(Message::eMessageError, "", "Chan import error: Cannot parse line from " + filename + ": '" + bufstr + "'", false);
851 
852                 return;
853             }
854         }
855     }
856     std::fclose(f);
857     _effect->beginEditBlock(kParamPosMatImportFile);
858     _translate->deleteAllKeys();
859     _rotate->deleteAllKeys();
860     if (_type == ePosMatCamera && _projection && _projection->_camFocalLength) {
861         _projection->_camFocalLength->deleteAllKeys();
862     }
863     for (std::list<ChanLine>::const_iterator it = lines.begin(); it != lines.end(); ++it) {
864         _translate->setValueAtTime(it->frame, it->tx, it->ty, it->tz);
865         _rotate->setValueAtTime(it->frame, it->rx, it->ry, it->rz);
866         if (_type == ePosMatCamera && _projection && _projection->_camFocalLength) {
867             double vaperture = _projection->_camVAperture->getValueAtTime(it->frame);
868             double focal = 0.5 * vaperture / std::tan(0.5 * (it->vfov * M_PI / 180));
869 
870             _projection->_camFocalLength->setValueAtTime(it->frame, focal);
871         }
872     }
873     _effect->endEditBlock();
874 }
875 
876 // importBoujou.
877 //
878 // Credits:
879 // - Ivan Busquets' importBoujou.py
880 // http://www.nukepedia.com/python/import/export/importboujou
881 // - Blenders' import_boujou.py
882 // https://wiki.blender.org/index.php/Extensions:2.4/Py/Scripts/Manual/Import/Boujou
883 // https://sourceforge.net/projects/boujouimport/
884 void
importBoujou()885 PosMatParam::importBoujou()
886 {
887     string filename;
888     _importFile->getValue(filename);
889     if ( filename.empty() ) {
890         // no filename, do nothing
891         return;
892     }
893     FILE* f = fopen_utf8(filename.c_str(), "r");
894     if (!f) {
895         _effect->sendMessage(Message::eMessageError, "", "Cannot read " + filename + ": " + std::strerror(errno), false);
896 
897         return;
898     }
899     bool foundOffset = false;
900     int offsetFrame = 1;
901     bool foundStart = false;
902     int startFrame;
903     double haperture = 0;
904     double vaperture = 0;
905     std::list<ChanLine> lines;
906     char buf[1024];
907     int i = 1; // line number
908 
909     while (std::fgets(buf, sizeof buf, f) != NULL) {
910         if (i == 1) {
911             const string b( trim(buf) );
912             const string h("# boujou export: text");
913             if (b != h) {
914                 _effect->sendMessage(Message::eMessageError, "", "Boujou import error: incorrect file header on first line, expected '" + h + "', got '" + b + "'", false);
915 
916                 std::fclose(f);
917                 return;
918             }
919         }
920 
921         std::stringstream ss(buf); // Insert the string into a stream
922 
923         std::vector<string> line; // Create vector to hold our words
924 
925         // split using whitespace
926         while (ss >> buf) {
927             line.push_back(buf);
928         }
929 
930         if (line.size() == 0) {
931             continue;
932         }
933         // # boujou export: text
934         // # Copyright (c) 2009, Vicon Motion Systems
935         // # boujou Version: 5.0.0 47534
936         // # Creation date : Thu Mar 17 17:30:38 2011
937         // # The image sequence file name was C:/Users/Nate's/Videos/final.mp4
938         // # boujou frame 0 is image sequence file 1000
939         // # boujou frame 100 is image sequence file 1100
940         // # One boujou frame for every image sequence frame
941         // # Exporting camera data for boujou frames 65 to 100
942         // # First boujou frame indexed to animation frame 1
943         //
944         //
945         // #The Camera (One line per time frame)
946         // #Image Size 1920 1080
947         // #Filmback Size 14.7574 8.3007
948         // #Line Format: Camera Rotation Matrix (9 numbers - 1st row, 2nd row, 3rd row) Camera Translation (3 numbers) Focal Length (mm)
949         // #rotation applied before translation
950         // #R(0,0) R(0,1) R(0,2) R(1,0) R(1,1) R(1,2) R(2,0) R(2,1) R(2,2) TxTy Tz F(mm)
951         // ...
952         //
953         // #3D Scene Points
954         // #x y z
955         // ...
956         // #End of boujou export file
957 
958         if (!foundOffset) {
959             // # Exporting camera data for boujou frames 65 to 100
960             if (line.size() == 10 && line[0] == "#" && line[1] == "Exporting" && line[line.size() - 2] == "to") {
961                 offsetFrame = std::atoi(line[line.size() - 3].c_str());
962                 foundOffset = true;
963            }
964         }
965         if (!foundStart) {
966             // # First boujou frame indexed to animation frame 1
967             if (line.size() == 9 && line[0] == "#" && line[1] == "First" && line[2] == "boujou" && line[3] == "frame") {
968                 startFrame = std::atoi(line[line.size() - 1].c_str()) + offsetFrame;
969                 foundStart = true;
970             }
971         }
972 
973         // #Filmback Size 14.7574 8.3007
974         if (line.size() == 4 && line[0] == "#Filmback") {
975             haperture = std::atof(line[2].c_str());
976             vaperture = std::atof(line[3].c_str());
977         }
978 
979         if (foundOffset && foundStart && line.size() == 13 && buf[0] != '#') {
980             ChanLine l;
981             //bool err = false;
982             l.frame = startFrame;
983             l.vfov = std::atof(line[12].c_str());
984             l.tx = std::atof(line[9].c_str());
985             l.ty = std::atof(line[10].c_str());
986             l.tz = std::atof(line[11].c_str());
987             double rot00 = std::atof(line[0].c_str());
988             double rot01 = std::atof(line[1].c_str());
989             double rot02 = std::atof(line[2].c_str());
990             double rot10 = std::atof(line[3].c_str());
991             //double rot11 = std::atof(line[4].c_str());
992             //double rot12 = std::atof(line[5].c_str());
993             double rot20 = std::atof(line[6].c_str());
994             double rot21 = std::atof(line[7].c_str());
995             double rot22 = std::atof(line[8].c_str());
996             if (rot00 == 0 && rot10 == 0) {
997                 l.rx = std::atan2(-rot01, -rot02) * 180. / M_PI;
998                 l.ry = 90.;
999                 l.rz = 0.;
1000             } else {
1001                 l.rx = std::atan2(rot21, -rot22) * 180. / M_PI;
1002                 l.ry = -std::asin(rot20) * 180. / M_PI;
1003                 l.rz = std::atan2(rot10,rot00) * 180. / M_PI;
1004             }
1005             lines.push_back(l);
1006             ++startFrame;
1007         }
1008         ++i;
1009     }
1010     std::fclose(f);
1011     _effect->beginEditBlock(kParamPosMatImportFile);
1012     _translate->deleteAllKeys();
1013     _rotate->deleteAllKeys();
1014     if (_type == ePosMatCamera && _projection) {
1015         if (_projection->_camFocalLength) {
1016             _projection->_camFocalLength->deleteAllKeys();
1017         }
1018         if (_projection->_camHAperture && haperture != 0.) {
1019             _projection->_camHAperture->deleteAllKeys();
1020             _projection->_camHAperture->setValue(haperture);
1021         }
1022         if (_projection->_camVAperture && vaperture != 0.) {
1023             _projection->_camVAperture->deleteAllKeys();
1024             _projection->_camVAperture->setValue(vaperture);
1025         }
1026     }
1027     for (std::list<ChanLine>::const_iterator it = lines.begin(); it != lines.end(); ++it) {
1028         _translate->setValueAtTime(it->frame, it->tx, it->ty, it->tz);
1029         _rotate->setValueAtTime(it->frame, it->rx, it->ry, it->rz);
1030         if (_type == ePosMatCamera && _projection && _projection->_camFocalLength) {
1031             // it-vfov contains focal in Boujou
1032             double focal = it->vfov;
1033             //double vaperture = _projection->_camVAperture->getValueAtTime(it->frame);
1034             //double focal = 0.5 * vaperture / std::tan(0.5 * (it->vfov * M_PI / 180));
1035 
1036             _projection->_camFocalLength->setValueAtTime(it->frame, focal);
1037         }
1038     }
1039     _effect->endEditBlock();
1040 }
1041 
1042 void
exportChan()1043 PosMatParam::exportChan()
1044 {
1045     string filename;
1046     _importFile->getValue(filename);
1047     if ( filename.empty() ) {
1048         // no filename, do nothing
1049         return;
1050     }
1051     FILE* f = fopen_utf8(filename.c_str(), "w");
1052     if (!f) {
1053         _effect->sendMessage(Message::eMessageError, "", "Cannot write " + filename + ": " + std::strerror(errno), false);
1054         return;
1055     }
1056     OfxRangeD r = _srcClip->getFrameRange();
1057     for (int t = (int)r.min; t <= (int)r.max; ++t) {
1058         ChanLine l;
1059         l.frame = t;
1060         _translate->getValueAtTime(t, l.tx, l.ty, l.tz);
1061         _rotate->getValueAtTime(t, l.rx, l.ry, l.rz);
1062         if (_type == ePosMatCamera && _projection && _projection->_camVAperture) {
1063             double vaperture = _projection->_camVAperture->getValueAtTime(t);
1064             double focal = _projection->_camFocalLength->getValueAtTime(t);
1065             l.vfov = 2 * std::atan2(0.5 * vaperture, focal) * 180 / M_PI;
1066             std::fprintf(f, "%d\t%g\t%g\t%g\t%g\t%g\t%g\t%g\n",
1067                          t, l.tx, l.ty, l.tz, l.rx, l.ry, l.rz, l.vfov);
1068         } else {
1069             std::fprintf(f, "%d\t%g\t%g\t%g\t%g\t%g\t%g\n",
1070                          t, l.tx, l.ty, l.tz, l.rx, l.ry, l.rz);
1071         }
1072     }
1073     std::fclose(f);
1074 }
1075 
1076 void
define(ImageEffectDescriptor & desc,PageParamDescriptor * page,GroupParamDescriptor * group,const std::string prefix,PosMatTypeEnum type)1077 PosMatParam::define(ImageEffectDescriptor &desc,
1078                     PageParamDescriptor *page,
1079                     GroupParamDescriptor *group,
1080                     const std::string prefix,
1081                     PosMatTypeEnum type)
1082 {
1083     {
1084         GroupParamDescriptor* subgroup = desc.defineGroupParam(prefix + kParamPosMatFile);
1085         if (subgroup) {
1086             subgroup->setLabelAndHint(kParamPosMatFileLabel);
1087             subgroup->setOpen(false);
1088             if (group) {
1089                 subgroup->setParent(*group);
1090             }
1091             if (page) {
1092                 page->addChild(*subgroup);
1093             }
1094         }
1095         {
1096             ChoiceParamDescriptor* param = desc.defineChoiceParam(prefix + kParamPosMatImportFormat);
1097             param->setLabelAndHint(kParamPosMatImportFormatLabel);
1098             assert(param->getNOptions() == eImportFormatChan);
1099             param->appendOption(kParamPosMatImportFormatOptionChan);
1100             assert(param->getNOptions() == eImportFormatBoujou);
1101             param->appendOption(kParamPosMatImportFormatOptionBoujou);
1102             if (subgroup) {
1103                 param->setParent(*subgroup);
1104             }
1105             if (page) {
1106                 page->addChild(*param);
1107             }
1108         }
1109         {
1110             StringParamDescriptor* param = desc.defineStringParam(prefix + kParamPosMatImportFile);
1111             param->setLabelAndHint(kParamPosMatImportFileLabel);
1112             param->setStringType(eStringTypeFilePath);
1113             param->setFilePathExists(true);
1114             param->setAnimates(false);
1115             param->setEvaluateOnChange(false);
1116             if (!OFX::getImageEffectHostDescription()->isNatron) { // Natron already has a reload button
1117                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
1118             }
1119             if (subgroup) {
1120                 param->setParent(*subgroup);
1121             }
1122             if (page) {
1123                 page->addChild(*param);
1124             }
1125 
1126         }
1127         if (!OFX::getImageEffectHostDescription()->isNatron) { // Natron already has a reload button
1128             PushButtonParamDescriptor* param = desc.definePushButtonParam(prefix + kParamPosMatImportFileReload);
1129             param->setLabelAndHint(kParamPosMatImportFileReloadLabel);
1130             if (subgroup) {
1131                 param->setParent(*subgroup);
1132             }
1133             if (page) {
1134                 page->addChild(*param);
1135             }
1136 
1137         }
1138         {
1139             StringParamDescriptor* param = desc.defineStringParam(prefix + kParamPosMatExportChan);
1140             param->setLabelAndHint(kParamPosMatExportChanLabel);
1141             param->setStringType(eStringTypeFilePath);
1142             param->setFilePathExists(false);
1143             param->setAnimates(false);
1144             param->setEvaluateOnChange(false);
1145             if (!OFX::getImageEffectHostDescription()->isNatron) { // Natron already has a rewrite button
1146                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
1147             }
1148             if (subgroup) {
1149                 param->setParent(*subgroup);
1150             }
1151             if (page) {
1152                 page->addChild(*param);
1153             }
1154 
1155         }
1156         if (!OFX::getImageEffectHostDescription()->isNatron) { // Natron already has a rewrite button
1157             PushButtonParamDescriptor* param = desc.definePushButtonParam(prefix + kParamPosMatExportChanRewrite);
1158             param->setLabelAndHint(kParamPosMatExportChanRewriteLabel);
1159             if (subgroup) {
1160                 param->setParent(*subgroup);
1161             }
1162             if (page) {
1163                 page->addChild(*param);
1164             }
1165 
1166         }
1167     }
1168     {
1169         ChoiceParamDescriptor* param = desc.defineChoiceParam(prefix + kParamPosMatTransformOrder);
1170         param->setLabelAndHint(kParamPosMatTransformOrderLabel);
1171         assert(param->getNOptions() == ePosMatTransformOrderSRT);
1172         param->appendOption(kParamPosMatTransformOrderOptionSRT);
1173         assert(param->getNOptions() == ePosMatTransformOrderSTR);
1174         param->appendOption(kParamPosMatTransformOrderOptionSTR);
1175         assert(param->getNOptions() == ePosMatTransformOrderRST);
1176         param->appendOption(kParamPosMatTransformOrderOptionRST);
1177         assert(param->getNOptions() == ePosMatTransformOrderRTS);
1178         param->appendOption(kParamPosMatTransformOrderOptionRTS);
1179         assert(param->getNOptions() == ePosMatTransformOrderTSR);
1180         param->appendOption(kParamPosMatTransformOrderOptionTSR);
1181         assert(param->getNOptions() == ePosMatTransformOrderTRS);
1182         param->appendOption(kParamPosMatTransformOrderOptionTRS);
1183         param->setDefault(kParamPosMatTransformOrderDefault);
1184         if (group) {
1185             param->setParent(*group);
1186         }
1187         if (page) {
1188             page->addChild(*param);
1189         }
1190     }
1191     {
1192         ChoiceParamDescriptor* param = desc.defineChoiceParam(prefix + kParamPosMatRotationOrder);
1193         param->setLabelAndHint(kParamPosMatRotationOrderLabel);
1194         assert(param->getNOptions() == ePosMatRotationOrderXYZ);
1195         param->appendOption(kParamPosMatRotationOrderOptionXYZ);
1196         assert(param->getNOptions() == ePosMatRotationOrderXZY);
1197         param->appendOption(kParamPosMatRotationOrderOptionXZY);
1198         assert(param->getNOptions() == ePosMatRotationOrderYXZ);
1199         param->appendOption(kParamPosMatRotationOrderOptionYXZ);
1200         assert(param->getNOptions() == ePosMatRotationOrderYZX);
1201         param->appendOption(kParamPosMatRotationOrderOptionYZX);
1202         assert(param->getNOptions() == ePosMatRotationOrderZXY);
1203         param->appendOption(kParamPosMatRotationOrderOptionZXY);
1204         assert(param->getNOptions() == ePosMatRotationOrderZYX);
1205         param->appendOption(kParamPosMatRotationOrderOptionZYX);
1206         param->setDefault(kParamPosMatRotationOrderDefault);
1207         if (group) {
1208             param->setParent(*group);
1209         }
1210         if (page) {
1211             page->addChild(*param);
1212         }
1213     }
1214     {
1215         Double3DParamDescriptor* param = desc.defineDouble3DParam(prefix + kParamPosMatTranslate);
1216         param->setLabelAndHint(kParamPosMatTranslateLabel);
1217         param->setRange(-DBL_MAX, -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX);
1218         param->setDisplayRange(-10., -10., -10., 10., 10., 10.);
1219         param->setDefault(0, 0, (type == ePosMatCard) ? -1 : 0.);
1220         if (group) {
1221             param->setParent(*group);
1222         }
1223         if (page) {
1224             page->addChild(*param);
1225         }
1226     }
1227     {
1228         Double3DParamDescriptor* param = desc.defineDouble3DParam(prefix + kParamPosMatRotate);
1229         param->setLabelAndHint(kParamPosMatRotateLabel);
1230         param->setRange(-DBL_MAX, -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX);
1231         param->setDisplayRange(-180., -180., -180., 180., 180., 180.);
1232         param->setDefault(0., 0., 0.);
1233         param->setDoubleType(eDoubleTypeAngle);
1234         if (group) {
1235             param->setParent(*group);
1236         }
1237         if (page) {
1238             page->addChild(*param);
1239         }
1240     }
1241     {
1242         Double3DParamDescriptor* param = desc.defineDouble3DParam(prefix + kParamPosMatScale);
1243         param->setLabelAndHint(kParamPosMatScaleLabel);
1244         param->setRange(-DBL_MAX, -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX);
1245         param->setDisplayRange(0.01, 0.01, 0.01, 10., 10., 10.);
1246         param->setDefault(1., 1., 1.);
1247         param->setDoubleType(eDoubleTypeScale);
1248         if (group) {
1249             param->setParent(*group);
1250         }
1251         if (page) {
1252             page->addChild(*param);
1253         }
1254     }
1255     {
1256         DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamPosMatUniformScale);
1257         param->setLabelAndHint(kParamPosMatUniformScaleLabel);
1258         param->setRange(-DBL_MAX, DBL_MAX);
1259         param->setDisplayRange(0.01, 10.);
1260         param->setDefault(1.);
1261         param->setDoubleType(eDoubleTypeScale);
1262         if (group) {
1263             param->setParent(*group);
1264         }
1265         if (page) {
1266             page->addChild(*param);
1267         }
1268     }
1269     {
1270         Double3DParamDescriptor* param = desc.defineDouble3DParam(prefix + kParamPosMatSkew);
1271         param->setLabelAndHint(kParamPosMatSkewLabel);
1272         param->setRange(-DBL_MAX, -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX);
1273         param->setDisplayRange(-1., -1., -1., 1., 1., 1.);
1274         param->setDefault(0., 0., 0.);
1275         if (group) {
1276             param->setParent(*group);
1277         }
1278         if (page) {
1279             page->addChild(*param);
1280         }
1281     }
1282     {
1283         Double3DParamDescriptor* param = desc.defineDouble3DParam(prefix + kParamPosMatPivot);
1284         param->setLabelAndHint(kParamPosMatPivotLabel);
1285         param->setRange(-DBL_MAX, -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX);
1286         param->setDisplayRange(-10., -10., -10., 10., 10., 10.);
1287         param->setDefault(0., 0., 0.);
1288         if (group) {
1289             param->setParent(*group);
1290         }
1291         if (page) {
1292             page->addChild(*param);
1293         }
1294     }
1295     {
1296         GroupParamDescriptor* subgroup = desc.defineGroupParam(prefix + kGroupPosMatLocalMatrix);
1297         if (subgroup) {
1298             subgroup->setLabel(kGroupPosMatLocalMatrixLabel);
1299             subgroup->setOpen(false);
1300             if (group) {
1301                 subgroup->setParent(*group);
1302             }
1303             if (page) {
1304                 page->addChild(*subgroup);
1305             }
1306         }
1307         {
1308             BooleanParamDescriptor* param = desc.defineBooleanParam(prefix + kParamPosMatUseMatrix);
1309             param->setLabelAndHint(kParamPosMatUseMatrixLabel);
1310             param->setAnimates(false);
1311             param->setEvaluateOnChange(false);
1312             if (subgroup) {
1313                 param->setParent(*subgroup);
1314             }
1315             if (page) {
1316                 page->addChild(*param);
1317             }
1318         }
1319         for (int i = 0; i < 4; ++i) {
1320             for (int j = 0; j < 4; ++j) {
1321                 DoubleParamDescriptor* param = desc.defineDoubleParam(prefix + kParamPosMatMatrix + (char)('1' + i) + (char)('1' + j));
1322                 param->setLabelAndHint(kParamPosMatMatrixLabel);
1323                 param->setRange(-DBL_MAX, DBL_MAX);
1324                 param->setDisplayRange(-1., 1.);
1325                 param->setDefault(i == j ? 1. : (i == 2 && j == 3) ? -1. : 0.);
1326                 if (j < 3) {
1327                     param->setLayoutHint(eLayoutHintNoNewLine, 1);
1328                 }
1329                 if (subgroup) {
1330                     param->setParent(*subgroup);
1331                 }
1332                 if (page) {
1333                     page->addChild(*param);
1334                 }
1335             }
1336         }
1337     }
1338     if (type == ePosMatCamera) {
1339         GroupParamDescriptor* subgroup = desc.defineGroupParam(kCameraCam kParamCameraProjectionGroup);
1340         if (subgroup) {
1341             subgroup->setLabel(kCameraCamLabel " " kParamCameraProjectionGroupLabel);
1342             subgroup->setOpen(false);
1343             if (group) {
1344                 subgroup->setParent(*group);
1345             }
1346             if (page) {
1347                 page->addChild(*subgroup);
1348             }
1349         }
1350 
1351         CameraParam::define(desc, page, subgroup, kCameraCam);
1352     }
1353 }
1354 
1355 void
getMatrix(const double t,Matrix4x4 * mat) const1356 PosMatParam::getMatrix(const double t, Matrix4x4* mat) const
1357 {
1358     if (_useMatrix->getValueAtTime(t)) {
1359         for (int i = 0; i < 4; ++i) {
1360             for (int j = 0; j < 4; ++j) {
1361                 (*mat)(i,j) = _matrix[i][j]->getValueAtTime(t);
1362             }
1363         }
1364 
1365         return;
1366     }
1367 
1368 
1369     PosMatTransformOrderEnum transformOrder = (PosMatTransformOrderEnum)_transformOrder->getValueAtTime(t);
1370     PosMatRotationOrderEnum rotationOrder = (PosMatRotationOrderEnum)_rotationOrder->getValueAtTime(t);
1371 
1372     Matrix4x4 T;
1373     T(0,0) = T(1,1) = T(2,2) = T(3,3) = 1.;
1374     _translate->getValueAtTime(t, T(0,3), T(1,3), T(2,3));
1375 
1376     double theta[3];
1377     _rotate->getValueAtTime(t, theta[0], theta[1], theta[2]);
1378     Matrix4x4 R;
1379     if (theta[0] == 0. && theta[1] == 0. && theta[2] == 0.) {
1380         R(0,0) = R(1,1) = R(2,2) = R(3,3) = 1.;
1381     } else {
1382         Matrix4x4 Rx, Ry, Rz;
1383         Rx(3,3) = Ry(3,3) = Rz(3,3) = 1.;
1384         {
1385             double s = std::sin(theta[0] * M_PI / 180.);
1386             double c = std::cos(theta[0] * M_PI / 180.);
1387             Rx(0,0) = 1.;
1388             Rx(1,1) = c; Rx(1,2) = -s;
1389             Rx(2,1) = s; Rx(2,2) = c;
1390         }
1391         {
1392             double s = std::sin(theta[1] * M_PI / 180.);
1393             double c = std::cos(theta[1] * M_PI / 180.);
1394             Ry(1,1) = 1.;
1395             Ry(2,2) = c; Ry(2,0) = -s;
1396             Ry(0,2) = s; Ry(0,0) = c;
1397         }
1398         {
1399             double s = std::sin(theta[2] * M_PI / 180.);
1400             double c = std::cos(theta[2] * M_PI / 180.);
1401             Rz(2,2) = 1.;
1402             Rz(0,0) = c; Rz(0,1) = -s;
1403             Rz(1,0) = s; Rz(1,1) = c;
1404         }
1405         switch (rotationOrder) {
1406             case ePosMatRotationOrderXYZ:
1407                 R = Rz * Ry * Rx;
1408                 break;
1409             case ePosMatRotationOrderXZY:
1410                 R = Ry * Rz * Rx;
1411                 break;
1412             case ePosMatRotationOrderYXZ:
1413                 R = Rz * Rx * Ry;
1414                 break;
1415             case ePosMatRotationOrderYZX:
1416                 R = Rx * Rz * Ry;
1417                 break;
1418             case ePosMatRotationOrderZXY:
1419             default:
1420                 R = Ry * Rx * Rz;
1421                 break;
1422             case ePosMatRotationOrderZYX:
1423                 R = Rx * Ry * Rz;
1424                 break;
1425         }
1426     }
1427 
1428     // in Nuke, skew is just before the rotation, xhatever the RTS order is (strange, but true)
1429     double skew[3];
1430     _skew->getValueAtTime(t, skew[0], skew[1], skew[2]);
1431     if (skew[0] != 0. ||
1432         skew[1] != 0. ||
1433         skew[2] != 0.) {
1434         Matrix4x4 K;
1435         K(0,1) = std::tan(skew[0] * M_PI / 180.);
1436         K(1,0) = std::tan(skew[1] * M_PI / 180.);
1437         K(1,2) = std::tan(skew[2] * M_PI / 180.);
1438         K(0,0) = K(1,1) = K(2,2) = K(3,3) = 1;
1439         R = R * K;
1440     }
1441 
1442     Matrix4x4 S;
1443     S(3,3) = 1.;
1444     _scale->getValueAtTime(t, S(0,0), S(1,1), S(2,2));
1445     {
1446         double uniformScale = _uniformScale->getValueAtTime(t);
1447         S(0,0) *= uniformScale;
1448         S(1,1) *= uniformScale;
1449         S(2,2) *= uniformScale;
1450 
1451     }
1452 
1453     switch (transformOrder) {
1454         case ePosMatTransformOrderSRT:
1455         default:
1456             *mat = T * R * S;
1457             break;
1458         case ePosMatTransformOrderSTR:
1459             *mat = R * T * S;
1460             break;
1461         case ePosMatTransformOrderRST:
1462             *mat = T * S * R;
1463             break;
1464         case ePosMatTransformOrderRTS:
1465             *mat = S * T * R;
1466             break;
1467         case ePosMatTransformOrderTSR:
1468             *mat = R * S * T;
1469             break;
1470         case ePosMatTransformOrderTRS:
1471             *mat = S * R * T;
1472             break;
1473     }
1474 
1475 
1476     // pivot
1477     double pivot[3];
1478     _pivot->getValueAtTime(t, pivot[0], pivot[1], pivot[2]);
1479     if (pivot[0] != 0. ||
1480         pivot[1] != 0. ||
1481         pivot[2] != 0.) {
1482         // (reuse the T matrix)
1483         T(0,3) = pivot[0];
1484         T(1,3) = pivot[1];
1485         T(2,3) = pivot[2];
1486         Matrix4x4 P;
1487         P(0,3) = -pivot[0];
1488         P(1,3) = -pivot[1];
1489         P(2,3) = -pivot[2];
1490         P(0,0) = P(1,1) = P(2,2) = P(3,3) = 1;
1491         *mat = T * *mat * P;
1492     }
1493 }
1494 
1495 void
changedParam(const InstanceChangedArgs & args,const std::string & paramName)1496 PosMatParam::changedParam(const InstanceChangedArgs &args,
1497                           const std::string &paramName)
1498 {
1499     if (paramName.compare(0, _prefix.length(), _prefix) != 0) {
1500         return;
1501     }
1502     const double t = args.time;
1503     if ( args.reason == eChangeUserEdit &&
1504         (paramName == _importFile->getName() ||
1505           (_importFileReload && paramName == _importFileReload->getName()) ) ) {
1506         ImportFormatEnum format = (ImportFormatEnum)_importFormat->getValue();
1507         switch (format) {
1508         case eImportFormatChan:
1509             importChan();
1510             break;
1511         case eImportFormatBoujou:
1512             importBoujou();
1513             break;
1514         }
1515     } else if ( args.reason == eChangeUserEdit &&
1516                 (paramName == _exportChan->getName() ||
1517                  (_exportChanRewrite && paramName == _exportChanRewrite->getName()) ) ) {
1518         exportChan();
1519     } else if ( paramName == _useMatrix->getName() ) {
1520         update();
1521     } else if (paramName == _transformOrder->getName() ||
1522                paramName == _rotationOrder->getName() ||
1523                paramName == _translate->getName() ||
1524                paramName == _rotate->getName() ||
1525                paramName == _scale->getName() ||
1526                paramName == _uniformScale->getName() ||
1527                paramName == _skew->getName() ||
1528                paramName == _pivot->getName() ||
1529                paramName == _useMatrix->getName()) {
1530         bool useMatrix = _useMatrix->getValueAtTime(t);
1531         if (!useMatrix) {
1532             Matrix4x4 mat;
1533             getMatrix(t, &mat);
1534             for (int i = 0; i < 4; ++i) {
1535                 for (int j = 0; j < 4; ++j) {
1536                     _matrix[i][j]->setValue( mat(i,j) );
1537                 }
1538             }
1539         }
1540     }
1541 }
1542 
1543 // END PosMatParm
1544 ////////////////////////////////////////////////////////////////////////////////
1545 
1546 
1547 ////////////////////////////////////////////////////////////////////////////////
1548 /** @brief The plugin that does our work */
1549 class Card3DPlugin
1550     : public Transform3x3Plugin
1551 {
1552 public:
1553     /** @brief ctor */
Card3DPlugin(OfxImageEffectHandle handle)1554     Card3DPlugin(OfxImageEffectHandle handle)
1555         : Transform3x3Plugin(handle, false, eTransform3x3ParamsTypeMotionBlur)
1556         //, _transformAmount(NULL)
1557         , _interactive(NULL)
1558         , _srcClipChanged(NULL)
1559         , _axisCamera(NULL)
1560         , _camCamera(NULL)
1561         , _axisPosMat(NULL)
1562         , _camEnable(NULL)
1563         , _camPosMat(NULL)
1564         , _card(this, kGroupCard, ePosMatCard)
1565         , _lensInFocal(NULL)
1566         , _lensInHAperture(NULL)
1567         , _extent(NULL)
1568         , _format(NULL)
1569         , _formatSize(NULL)
1570         , _formatPar(NULL)
1571         , _btmLeft(NULL)
1572         , _size(NULL)
1573         , _recenter(NULL)
1574     {
1575         if (getImageEffectHostDescription()->supportsCamera) {
1576             _axisCamera = fetchCamera(kCameraAxis);
1577             _camCamera = fetchCamera(kCameraCam);
1578         } else {
1579             _axisPosMat = new PosMatParam(this, kCameraAxis, ePosMatAxis);
1580             _camEnable = fetchBooleanParam(kParamCamEnable);
1581             _camPosMat = new PosMatParam(this, kCameraCam, ePosMatCamera);
1582         }
1583         _lensInFocal = fetchDoubleParam(kParamLensInFocal);
1584         _lensInHAperture = fetchDoubleParam(kParamLensInHAperture);
1585         _extent = fetchChoiceParam(kParamOutputFormat);
1586         _format = fetchChoiceParam(kParamGeneratorFormat);
1587         _formatSize = fetchInt2DParam(kParamGeneratorSize);
1588         _formatPar= fetchDoubleParam(kParamGeneratorPAR);
1589         _btmLeft = fetchDouble2DParam(kParamRectangleInteractBtmLeft);
1590         _size = fetchDouble2DParam(kParamRectangleInteractSize);
1591         _recenter = fetchPushButtonParam(kParamGeneratorCenter);
1592 
1593         //_transformAmount = fetchDoubleParam(kParamTransformAmount);
1594         _interactive = fetchBooleanParam(kParamTransformInteractive);
1595         assert(_interactive);
1596         _srcClipChanged = fetchBooleanParam(kParamSrcClipChanged);
1597         assert(_srcClipChanged);
1598 
1599         // honor kParamDefaultsNormalised
1600         if ( paramExists(kParamDefaultsNormalised) ) {
1601             // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
1602             // handle these ourselves!
1603             BooleanParam* param = fetchBooleanParam(kParamDefaultsNormalised);
1604             assert(param);
1605             bool normalised = param->getValue();
1606             if (normalised) {
1607                 OfxPointD size = getProjectExtent();
1608                 OfxPointD origin = getProjectOffset();
1609                 OfxPointD p;
1610                 // we must denormalise all parameters for which setDefaultCoordinateSystem(eCoordinatesNormalised) couldn't be done
1611                 beginEditBlock(kParamDefaultsNormalised);
1612                 p = _btmLeft->getValue();
1613                 _btmLeft->setValue(p.x * size.x + origin.x, p.y * size.y + origin.y);
1614                 p = _size->getValue();
1615                 _size->setValue(p.x * size.x, p.y * size.y);
1616                 param->setValue(false);
1617                 endEditBlock();
1618             }
1619         }
1620 
1621         // finally...
1622         syncPrivateData();
1623     }
1624 
~Card3DPlugin()1625     ~Card3DPlugin()
1626     {
1627         delete _axisPosMat;
1628         delete _camPosMat;
1629     }
1630 
1631 private:
1632     //virtual bool getRegionOfDefinition(const RegionOfDefinitionArguments &args, OfxRectD &rod) OVERRIDE FINAL;
1633     virtual void getClipPreferences(ClipPreferencesSetter &clipPreferences) OVERRIDE FINAL;
1634     virtual bool isIdentity(double time) OVERRIDE FINAL;
1635     virtual bool getInverseTransformCanonical(double time, int view, double amount, bool invert, Matrix3x3* invtransform) const OVERRIDE FINAL;
1636 
1637     virtual void changedParam(const InstanceChangedArgs &args, const std::string &paramName) OVERRIDE FINAL;
1638 
1639     /** @brief called when a clip has just been changed in some way (a rewire maybe) */
1640     virtual void changedClip(const InstanceChangedArgs &args, const std::string &clipName) OVERRIDE FINAL;
1641     /** @brief The sync private data action, called when the effect needs to sync any private data to persistent parameters */
1642     virtual void syncPrivateData(void) OVERRIDE FINAL;
1643 
1644     void updateVisibility();
1645 
1646     bool getOutputFormat(double time,
1647                          const OfxPointD& renderScale,
1648                          OfxRectI *format,
1649                          double *par) const;
1650     // NON-GENERIC
1651     //DoubleParam* _transformAmount;
1652     BooleanParam* _interactive;
1653     BooleanParam* _srcClipChanged; // set to true the first time the user connects src
1654     Camera* _axisCamera;
1655     Camera* _camCamera;
1656     PosMatParam* _axisPosMat;
1657     BooleanParam* _camEnable;
1658     PosMatParam* _camPosMat;
1659     PosMatParam _card;
1660     DoubleParam* _lensInFocal;
1661     DoubleParam* _lensInHAperture;
1662 
1663     // format params
1664     ChoiceParam* _extent;
1665     ChoiceParam* _format;
1666     Int2DParam* _formatSize;
1667     DoubleParam* _formatPar;
1668     Double2DParam* _btmLeft;
1669     Double2DParam* _size;
1670     PushButtonParam *_recenter;
1671 };
1672 
1673 void
syncPrivateData()1674 Card3DPlugin::syncPrivateData()
1675 {
1676     updateVisibility();
1677 }
1678 
1679 // returns true if fixed format (i.e. not the input RoD) and setFormat can be called in getClipPrefs
1680 bool
getOutputFormat(double,const OfxPointD & renderScale,OfxRectI * format,double * par) const1681 Card3DPlugin::getOutputFormat(double /*time*/,
1682                               const OfxPointD& renderScale,
1683                               OfxRectI *format,
1684                               double *par) const
1685 {
1686     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
1687 
1688     switch (extent) {
1689         case eGeneratorExtentFormat: {
1690             int w, h;
1691             _formatSize->getValue(w, h);
1692             *par = _formatPar->getValue();
1693             format->x1 = format->y1 = 0;
1694             format->x2 = std::ceil(w * renderScale.x);
1695             format->y2 = std::ceil(h * renderScale.y);
1696 
1697             return true;
1698             break;
1699         }
1700         case eGeneratorExtentSize: {
1701             OfxRectD rod;
1702             _size->getValue(rod.x2, rod.y2);
1703             _btmLeft->getValue(rod.x1, rod.y1);
1704             rod.x2 += rod.x1;
1705             rod.y2 += rod.y1;
1706             *par = _srcClip ? _srcClip->getPixelAspectRatio() : 1.;
1707             Coords::toPixelNearest(rod, renderScale, *par, format);
1708 
1709             return true;
1710             break;
1711         }
1712         case eGeneratorExtentProject: {
1713             OfxRectD rod;
1714             OfxPointD siz = getProjectSize();
1715             OfxPointD off = getProjectOffset();
1716             rod.x1 = off.x;
1717             rod.x2 = off.x + siz.x;
1718             rod.y1 = off.y;
1719             rod.y2 = off.y + siz.y;
1720             *par = getProjectPixelAspectRatio();
1721             Coords::toPixelNearest(rod, renderScale, *par, format);
1722 
1723             return true;
1724             break;
1725         }
1726         case eGeneratorExtentDefault:
1727         default:
1728             assert(false);
1729             /*
1730             if ( _srcClip && _srcClip->isConnected() ) {
1731                 format->x1 = format->y1 = format->x2 = format->y2 = 0; // default value
1732                 _srcClip->getFormat(*format);
1733                 *par = _srcClip->getPixelAspectRatio();
1734                 if ( OFX::Coords::rectIsEmpty(*format) ) {
1735                     // no format is available, use the RoD instead
1736                     const OfxRectD& srcRod = _srcClip->getRegionOfDefinition(time);
1737                     Coords::toPixelNearest(srcRod, renderScale, *par, format);
1738                 }
1739             } else {
1740                 // default to Project Size
1741                 OfxRectD srcRoD;
1742                 OfxPointD siz = getProjectSize();
1743                 OfxPointD off = getProjectOffset();
1744                 srcRoD.x1 = off.x;
1745                 srcRoD.x2 = off.x + siz.x;
1746                 srcRoD.y1 = off.y;
1747                 srcRoD.y2 = off.y + siz.y;
1748                 *par = getProjectPixelAspectRatio();
1749                 Coords::toPixelNearest(srcRoD, renderScale, *par, format);
1750             }
1751              */
1752             return false;
1753             break;
1754     }
1755     return false;
1756 }
1757 
1758 #if 0 // getRoD is done by ofxsTransform3x3
1759 bool
1760 Card3DPlugin::getRegionOfDefinition(const RegionOfDefinitionArguments &args, OfxRectD &rod)
1761 {
1762     const double time = args.time;
1763     OfxRectI format = {0, 1, 0, 1};
1764     double par = 1.;
1765     getOutputFormat(time, args.renderScale, &format, &par);
1766     const OfxPointD rs1 = {1., 1.};
1767     OFX::Coords::toCanonical(format, rs1, par, &rod);
1768 
1769     return true;
1770 }
1771 #endif
1772 
1773 void
getClipPreferences(ClipPreferencesSetter & clipPreferences)1774 Card3DPlugin::getClipPreferences(ClipPreferencesSetter &clipPreferences)
1775 {
1776     //We have to do this because the processing code does not support varying components for uvClip and srcClip
1777     PixelComponentEnum dstPixelComps = getDefaultOutputClipComponents();
1778 
1779     if (_srcClip) {
1780         clipPreferences.setClipComponents(*_srcClip, dstPixelComps);
1781     }
1782     OfxRectI format;
1783     double par;
1784     OfxPointD renderScale = {1., 1.};
1785 
1786     // we pass 0 as time, since anyway the input RoD is never used, thanks to the test on the return value
1787     bool setFormat = getOutputFormat(0, renderScale, &format, &par);
1788     if (setFormat) {
1789         clipPreferences.setOutputFormat(format);
1790         clipPreferences.setPixelAspectRatio(*_dstClip, par);
1791     }
1792 }
1793 
1794 // overridden is identity
1795 bool
isIdentity(double)1796 Card3DPlugin::isIdentity(double /*time*/)
1797 {
1798     // NON-GENERIC
1799     //double amount = _transformAmount->getValueAtTime(time);
1800     //if (amount == 0.) {
1801     //    return true;
1802     //}
1803 
1804     return false;
1805 }
1806 
1807 bool
getInverseTransformCanonical(double time,int view,double,bool invert,Matrix3x3 * invtransform) const1808 Card3DPlugin::getInverseTransformCanonical(double time,
1809                                               int view,
1810                                               double /*amount*/,
1811                                               bool invert,
1812                                               Matrix3x3* invtransform) const
1813 {
1814     Matrix4x4 axis;
1815     if (_axisCamera) {
1816         if (_axisCamera->isConnected()) {
1817             _axisCamera->getParameter(kNukeOfxCameraParamPositionMatrix, time, view, &axis(0,0), 16);
1818         } else {
1819             axis(0, 0) = axis(1,1) = axis(2,2) = axis(3,3) = 1.;
1820         }
1821     } else {
1822         _axisPosMat->getMatrix(time, &axis);
1823     }
1824     Matrix4x4 cam;
1825     CameraProjectionModeEnum camProjectionMode = eCameraProjectionModePerspective;
1826     double camFocal = 1.;
1827     double camHAperture = 1.; // only the ratio focal/haperture matters for card3d
1828     double camWinTranslate[2] = {0., 0.};
1829     double camWinScale[2] = {1., 1.};
1830     double camWinRoll = 0.;
1831     if (_camCamera) {
1832         if (_camCamera->isConnected()) {
1833             _camCamera->getParameter(kNukeOfxCameraParamPositionMatrix, time, view, &cam(0,0), 16);
1834             double projectionMode;
1835             _camCamera->getParameter(kNukeOfxCameraParamProjectionMode, time, view, &projectionMode, 1);
1836             camProjectionMode = (CameraProjectionModeEnum)((int)projectionMode);
1837             _camCamera->getParameter(kNukeOfxCameraParamFocalLength, time, view, &camFocal, 1);
1838             _camCamera->getParameter(kNukeOfxCameraParamHorizontalAperture, time, view, &camHAperture, 1);
1839             _camCamera->getParameter(kNukeOfxCameraParamWindowTranslate, time, view, camWinTranslate, 2);
1840             _camCamera->getParameter(kNukeOfxCameraParamWindowScale, time, view, camWinScale, 2);
1841             _camCamera->getParameter(kNukeOfxCameraParamWindowRoll, time, view, &camWinRoll, 1);
1842         }
1843     } else if (_camEnable->getValueAtTime(time)) {
1844         _camPosMat->getMatrix(time, &cam);
1845         _camPosMat->getProjection().getValueAtTime(time, camProjectionMode, camFocal, camHAperture, camWinTranslate[0], camWinTranslate[1], camWinScale[0], camWinScale[1], camWinRoll);
1846     } else {
1847         cam(0,0) = cam(1,1) = cam(2,2) = cam(3,3) = 1.;
1848     }
1849     Matrix4x4 card;
1850     _card.getMatrix(time, &card);
1851 
1852 
1853     // compose matrices
1854     Matrix4x4 invCam;
1855     if ( !cam.inverse(&invCam) ) {
1856         invCam(0,0) = invCam(1,1) = invCam(2,2) = invCam(3,3) = 1.;
1857     }
1858     Matrix4x4 pos = invCam * axis * card;
1859 
1860     // apply camera params
1861     Matrix3x3 mat;
1862     CameraParam::getMatrix(pos, camProjectionMode, camFocal, camHAperture, camWinTranslate[0], camWinTranslate[1], camWinScale[0], camWinScale[1], camWinRoll, &mat);
1863 
1864     double lensInFocal = _lensInFocal->getValueAtTime(time);
1865     double lensInHAperture = _lensInHAperture->getValueAtTime(time);
1866     double a = lensInHAperture / std::max(1e-8, lensInFocal);
1867     mat(0,0) *= a;
1868     mat(1,0) *= a;
1869     mat(2,0) *= a;
1870     mat(0,1) *= a;
1871     mat(1,1) *= a;
1872     mat(2,1) *= a;
1873 
1874     // mat is the direct transform, from source coords to output coords.
1875     // it is normalized for coordinates in (-0.5,0.5)x(-0.5*h/w,0.5*h/w) with y from to to bottom
1876 
1877     // get the input format (Natron only) or the input RoD (others)
1878     OfxRectD srcFormatCanonical;
1879     {
1880         OfxRectI srcFormat;
1881         _srcClip->getFormat(srcFormat);
1882         double par = _srcClip->getPixelAspectRatio();
1883         if ( OFX::Coords::rectIsEmpty(srcFormat) ) {
1884             // no format is available, use the RoD instead
1885             srcFormatCanonical = _srcClip->getRegionOfDefinition(time);
1886         } else {
1887             const OfxPointD rs1 = {1., 1.};
1888             Coords::toCanonical(srcFormat, rs1, par, &srcFormatCanonical);
1889         }
1890     }
1891 
1892     OfxRectI dstFormat = {0, 1, 0, 1};
1893     double dstPar = 1.;
1894     const OfxPointD rs1 = {1., 1.};
1895     getOutputFormat(time, rs1, &dstFormat, &dstPar);
1896     OfxRectD dstFormatCanonical;
1897     OFX::Coords::toCanonical(dstFormat, rs1, dstPar, &dstFormatCanonical);
1898 
1899     Matrix3x3 N; // normalize source
1900     {
1901         double w = srcFormatCanonical.x2 - srcFormatCanonical.x1;
1902         //double h = srcFormatCanonical.y2 - srcFormatCanonical.y1;
1903         if (w == 0.) {
1904             return false;
1905         }
1906         N(0,0) = 1./w;
1907         N(0,2) = -(srcFormatCanonical.x1 + srcFormatCanonical.x2) / (2. * w);
1908         N(1,1) = 1./w;
1909         N(1,2) = -(srcFormatCanonical.y1 + srcFormatCanonical.y2) / (2. * w);
1910         N(2,2) = 1.;
1911     }
1912 
1913     Matrix3x3 D; // denormalize output
1914     {
1915         double w = dstFormatCanonical.x2 - dstFormatCanonical.x1;
1916         //double h = dstFormatCanonical.y2 - dstFormatCanonical.y1;
1917         D(0,0) = -w;
1918         D(0,2) = (dstFormatCanonical.x1 + dstFormatCanonical.x2) / 2.;
1919         D(1,1) = -w;
1920         D(1,2) = (dstFormatCanonical.y1 + dstFormatCanonical.y2) / 2.;
1921         D(2,2) = 1.;
1922     }
1923 
1924     mat = D * mat * N;
1925 
1926     if (invert) {
1927         (*invtransform) = mat;
1928     } else {
1929         if ( !mat.inverse(invtransform) ) {
1930             return false;
1931         }
1932     }
1933 
1934     return true;
1935 } // Card3DPlugin::getInverseTransformCanonical
1936 
1937 
1938 void
updateVisibility()1939 Card3DPlugin::updateVisibility()
1940 {
1941     if (_camEnable) {
1942         bool enabled = _camEnable->getValue();
1943         _camPosMat->setEnabled(enabled);
1944     }
1945     {
1946         GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
1947         bool hasFormat = (extent == eGeneratorExtentFormat);
1948         bool hasSize = (extent == eGeneratorExtentSize);
1949 
1950         _format->setIsSecretAndDisabled(!hasFormat);
1951         _size->setIsSecretAndDisabled(!hasSize);
1952         _recenter->setIsSecretAndDisabled(!hasSize);
1953         _btmLeft->setIsSecretAndDisabled(!hasSize);
1954     }
1955 }
1956 
1957 void
changedParam(const InstanceChangedArgs & args,const std::string & paramName)1958 Card3DPlugin::changedParam(const InstanceChangedArgs &args,
1959                            const std::string &paramName)
1960 {
1961     //const double time = args.time;
1962     if ( (paramName == kParamPremult) && (args.reason == eChangeUserEdit) ) {
1963         _srcClipChanged->setValue(true);
1964     } else if (paramName == kParamCamEnable) {
1965         updateVisibility();
1966     } else if (paramName == kParamOutputFormat) {
1967         updateVisibility();
1968     } else if (paramName == kParamGeneratorFormat) {
1969         //the host does not handle the format itself, do it ourselves
1970         EParamFormat format = (EParamFormat)_format->getValue();
1971         int w = 0, h = 0;
1972         double par = -1;
1973         getFormatResolution(format, &w, &h, &par);
1974         assert(par != -1);
1975         _formatPar->setValue(par);
1976         _formatSize->setValue(w, h);
1977     } else if (paramName == kParamGeneratorCenter) {
1978         Clip* srcClip = _srcClip;
1979         OfxRectD srcRoD;
1980         if ( srcClip && srcClip->isConnected() ) {
1981             srcRoD = srcClip->getRegionOfDefinition(args.time);
1982         } else {
1983             OfxPointD siz = getProjectSize();
1984             OfxPointD off = getProjectOffset();
1985             srcRoD.x1 = off.x;
1986             srcRoD.x2 = off.x + siz.x;
1987             srcRoD.y1 = off.y;
1988             srcRoD.y2 = off.y + siz.y;
1989         }
1990         OfxPointD center;
1991         center.x = (srcRoD.x2 + srcRoD.x1) / 2.;
1992         center.y = (srcRoD.y2 + srcRoD.y1) / 2.;
1993 
1994         OfxRectD rectangle;
1995         _size->getValue(rectangle.x2, rectangle.y2);
1996         _btmLeft->getValue(rectangle.x1, rectangle.y1);
1997         rectangle.x2 += rectangle.x1;
1998         rectangle.y2 += rectangle.y1;
1999 
2000         OfxRectD newRectangle;
2001         newRectangle.x1 = center.x - (rectangle.x2 - rectangle.x1) / 2.;
2002         newRectangle.y1 = center.y - (rectangle.y2 - rectangle.y1) / 2.;
2003         newRectangle.x2 = newRectangle.x1 + (rectangle.x2 - rectangle.x1);
2004         newRectangle.y2 = newRectangle.y1 + (rectangle.y2 - rectangle.y1);
2005 
2006         _size->setValue(newRectangle.x2 - newRectangle.x1, newRectangle.y2 - newRectangle.y1);
2007         _btmLeft->setValue(newRectangle.x1, newRectangle.y1);
2008     } else {
2009         if (_axisPosMat) {
2010             _axisPosMat->changedParam(args, paramName);
2011         }
2012         if (_camPosMat) {
2013             _camPosMat->changedParam(args, paramName);
2014         }
2015         _card.changedParam(args, paramName);
2016         Transform3x3Plugin::changedParam(args, paramName);
2017     }
2018 }
2019 
2020 void
changedClip(const InstanceChangedArgs & args,const std::string & clipName)2021 Card3DPlugin::changedClip(const InstanceChangedArgs &args,
2022                              const std::string &clipName)
2023 {
2024     if ( (clipName == kOfxImageEffectSimpleSourceClipName) &&
2025          _srcClip && _srcClip->isConnected() &&
2026          ( args.reason == eChangeUserEdit) ) {
2027         //resetCenter(args.time);
2028     }
2029 }
2030 
2031 mDeclarePluginFactory(Card3DPluginFactory, {ofxsThreadSuiteCheck();}, {});
2032 
2033 
2034 void
describe(ImageEffectDescriptor & desc)2035 Card3DPluginFactory::describe(ImageEffectDescriptor &desc)
2036 {
2037     // basic labels
2038     desc.setLabel(kPluginName);
2039     desc.setPluginGrouping(kPluginGrouping);
2040     desc.setPluginDescription(kPluginDescription);
2041 
2042     Transform3x3Describe(desc, false);
2043 
2044     //desc.setOverlayInteractDescriptor(new TransformOverlayDescriptorOldParams);
2045 }
2046 
2047 
2048 
2049 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)2050 Card3DPluginFactory::describeInContext(ImageEffectDescriptor &desc,
2051                                           ContextEnum context)
2052 {
2053     // make some pages and to things in
2054     PageParamDescriptor *page = Transform3x3DescribeInContextBegin(desc, context, false);
2055 
2056     if (getImageEffectHostDescription()->supportsCamera) {
2057         {
2058             CameraDescriptor* camera = desc.defineCamera(kCameraCam);
2059             camera->setLabel(kCameraCamLabel);
2060             camera->setOptional(true);
2061         }
2062         {
2063             CameraDescriptor* camera = desc.defineCamera(kCameraAxis);
2064             camera->setLabel(kCameraAxisLabel);
2065             camera->setOptional(true);
2066         }
2067     } else {
2068         {
2069             GroupParamDescriptor* group = desc.defineGroupParam(kCameraAxis);
2070             group->setLabel(kCameraAxisLabel);
2071             group->setOpen(false);
2072             if (page) {
2073                 page->addChild(*group);
2074             }
2075             PosMatParam::define(desc, page, group, kCameraAxis, ePosMatAxis);
2076         }
2077         {
2078             GroupParamDescriptor* group = desc.defineGroupParam(kCameraCam);
2079             if (group) {
2080                 group->setLabel(kCameraCamLabel);
2081                 group->setOpen(false);
2082                 if (page) {
2083                     page->addChild(*group);
2084                 }
2085             }
2086             {
2087                 BooleanParamDescriptor* param = desc.defineBooleanParam(kParamCamEnable);
2088                 param->setLabelAndHint(kParamCamEnableLabel);
2089                 param->setDefault(false);
2090                 param->setAnimates(false);
2091                 if (group) {
2092                     param->setParent(*group);
2093                 }
2094                 if (page) {
2095                     page->addChild(*param);
2096                 }
2097             }
2098 
2099             PosMatParam::define(desc, page, group, kCameraCam, ePosMatCamera);
2100         }
2101     }
2102 
2103     PosMatParam::define(desc, page, /*group=*/NULL, /*prefix=*/kGroupCard, ePosMatCard);
2104 
2105     {
2106         DoubleParamDescriptor* param = desc.defineDoubleParam(kParamLensInFocal);
2107         param->setLabelAndHint(kParamLensInFocalLabel);
2108         param->setDefault(1.);
2109         param->setRange(1e-8, DBL_MAX);
2110         param->setDisplayRange(1e-8, 1.);
2111         if (page) {
2112             page->addChild(*param);
2113         }
2114     }
2115 
2116     {
2117         DoubleParamDescriptor* param = desc.defineDoubleParam(kParamLensInHAperture);
2118         param->setLabelAndHint(kParamLensInHApertureLabel);
2119         param->setDefault(1.);
2120         param->setRange(1e-8, DBL_MAX);
2121         param->setDisplayRange(1e-8, 1.);
2122         param->setLayoutHint(eLayoutHintDivider);
2123         if (page) {
2124             page->addChild(*param);
2125         }
2126     }
2127 
2128     { // Frame format
2129         // extent
2130         {
2131             ChoiceParamDescriptor* param = desc.defineChoiceParam(kParamOutputFormat);
2132             param->setLabelAndHint(kParamOutputFormatLabel);
2133             assert(param->getNOptions() == eGeneratorExtentFormat);
2134             param->appendOption(kParamGeneratorExtentOptionFormat);
2135             assert(param->getNOptions() == eGeneratorExtentSize);
2136             param->appendOption(kParamGeneratorExtentOptionSize);
2137             assert(param->getNOptions() == eGeneratorExtentProject);
2138             param->appendOption(kParamGeneratorExtentOptionProject);
2139             //assert(param->getNOptions() == eGeneratorExtentDefault);
2140             //param->appendOption(kParamGeneratorExtentOptionDefault);
2141             param->setDefault(eGeneratorExtentProject);
2142             param->setLayoutHint(eLayoutHintNoNewLine, 1);
2143             param->setAnimates(false);
2144             desc.addClipPreferencesSlaveParam(*param);
2145             if (page) {
2146                 page->addChild(*param);
2147             }
2148         }
2149 
2150         // recenter
2151         {
2152             PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamGeneratorCenter);
2153             param->setLabel(kParamGeneratorCenterLabel);
2154             param->setHint(kParamGeneratorCenterHint);
2155             param->setLayoutHint(eLayoutHintNoNewLine, 1);
2156             if (page) {
2157                 page->addChild(*param);
2158             }
2159         }
2160 
2161         // format
2162         {
2163             ChoiceParamDescriptor* param = desc.defineChoiceParam(kParamGeneratorFormat);
2164             param->setLabel(kParamGeneratorFormatLabel);
2165             assert(param->getNOptions() == eParamFormatPCVideo);
2166             param->appendOption(kParamFormatPCVideoLabel, "", kParamFormatPCVideo);
2167             assert(param->getNOptions() == eParamFormatNTSC);
2168             param->appendOption(kParamFormatNTSCLabel, "", kParamFormatNTSC);
2169             assert(param->getNOptions() == eParamFormatPAL);
2170             param->appendOption(kParamFormatPALLabel, "", kParamFormatPAL);
2171             assert(param->getNOptions() == eParamFormatNTSC169);
2172             param->appendOption(kParamFormatNTSC169Label, "", kParamFormatNTSC169);
2173             assert(param->getNOptions() == eParamFormatPAL169);
2174             param->appendOption(kParamFormatPAL169Label, "", kParamFormatPAL169);
2175             assert(param->getNOptions() == eParamFormatHD720);
2176             param->appendOption(kParamFormatHD720Label, "", kParamFormatHD720);
2177             assert(param->getNOptions() == eParamFormatHD);
2178             param->appendOption(kParamFormatHDLabel, "", kParamFormatHD);
2179             assert(param->getNOptions() == eParamFormatUHD4K);
2180             param->appendOption(kParamFormatUHD4KLabel, "", kParamFormatUHD4K);
2181             assert(param->getNOptions() == eParamFormat1kSuper35);
2182             param->appendOption(kParamFormat1kSuper35Label, "", kParamFormat1kSuper35);
2183             assert(param->getNOptions() == eParamFormat1kCinemascope);
2184             param->appendOption(kParamFormat1kCinemascopeLabel, "", kParamFormat1kCinemascope);
2185             assert(param->getNOptions() == eParamFormat2kSuper35);
2186             param->appendOption(kParamFormat2kSuper35Label, "", kParamFormat2kSuper35);
2187             assert(param->getNOptions() == eParamFormat2kCinemascope);
2188             param->appendOption(kParamFormat2kCinemascopeLabel, "", kParamFormat2kCinemascope);
2189             assert(param->getNOptions() == eParamFormat2kDCP);
2190             param->appendOption(kParamFormat2kDCPLabel, "", kParamFormat2kDCP);
2191             assert(param->getNOptions() == eParamFormat4kSuper35);
2192             param->appendOption(kParamFormat4kSuper35Label, "", kParamFormat4kSuper35);
2193             assert(param->getNOptions() == eParamFormat4kCinemascope);
2194             param->appendOption(kParamFormat4kCinemascopeLabel, "", kParamFormat4kCinemascope);
2195             assert(param->getNOptions() == eParamFormat4kDCP);
2196             param->appendOption(kParamFormat4kDCPLabel, "", kParamFormat4kDCP);
2197             assert(param->getNOptions() == eParamFormatSquare256);
2198             param->appendOption(kParamFormatSquare256Label, "", kParamFormatSquare256);
2199             assert(param->getNOptions() == eParamFormatSquare512);
2200             param->appendOption(kParamFormatSquare512Label, "", kParamFormatSquare512);
2201             assert(param->getNOptions() == eParamFormatSquare1k);
2202             param->appendOption(kParamFormatSquare1kLabel, "", kParamFormatSquare1k);
2203             assert(param->getNOptions() == eParamFormatSquare2k);
2204             param->appendOption(kParamFormatSquare2kLabel, "", kParamFormatSquare2k);
2205             param->setDefault(eParamFormatPCVideo);
2206             param->setHint(kParamGeneratorFormatHint);
2207             param->setAnimates(false);
2208             desc.addClipPreferencesSlaveParam(*param);
2209             if (page) {
2210                 page->addChild(*param);
2211             }
2212         }
2213 
2214         {
2215             int w = 0, h = 0;
2216             double par = -1.;
2217             getFormatResolution(eParamFormatPCVideo, &w, &h, &par);
2218             assert(par != -1);
2219             {
2220                 Int2DParamDescriptor* param = desc.defineInt2DParam(kParamGeneratorSize);
2221                 param->setLabel(kParamGeneratorSizeLabel);
2222                 param->setHint(kParamGeneratorSizeHint);
2223                 param->setIsSecretAndDisabled(true);
2224                 param->setDefault(w, h);
2225                 if (page) {
2226                     page->addChild(*param);
2227                 }
2228             }
2229 
2230             {
2231                 DoubleParamDescriptor* param = desc.defineDoubleParam(kParamGeneratorPAR);
2232                 param->setLabel(kParamGeneratorPARLabel);
2233                 param->setHint(kParamGeneratorPARHint);
2234                 param->setIsSecretAndDisabled(true);
2235                 param->setRange(0., DBL_MAX);
2236                 param->setDisplayRange(0.5, 2.);
2237                 param->setDefault(par);
2238                 if (page) {
2239                     page->addChild(*param);
2240                 }
2241             }
2242         }
2243 
2244         // btmLeft
2245         {
2246             Double2DParamDescriptor* param = desc.defineDouble2DParam(kParamRectangleInteractBtmLeft);
2247             param->setLabel(kParamRectangleInteractBtmLeftLabel);
2248             param->setDoubleType(eDoubleTypeXYAbsolute);
2249             if ( param->supportsDefaultCoordinateSystem() ) {
2250                 param->setDefaultCoordinateSystem(eCoordinatesNormalised); // no need of kParamDefaultsNormalised
2251             } else {
2252                 gHostSupportsDefaultCoordinateSystem = false; // no multithread here, see kParamDefaultsNormalised
2253             }
2254             param->setDefault(0., 0.);
2255             param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
2256             param->setDisplayRange(-10000, -10000, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
2257             param->setIncrement(1.);
2258             param->setLayoutHint(eLayoutHintNoNewLine, 1);
2259             param->setHint("Coordinates of the bottom left corner of the size rectangle.");
2260             param->setDigits(0);
2261             if (page) {
2262                 page->addChild(*param);
2263             }
2264         }
2265 
2266         // size
2267         {
2268             Double2DParamDescriptor* param = desc.defineDouble2DParam(kParamRectangleInteractSize);
2269             param->setLabel(kParamRectangleInteractSizeLabel);
2270             param->setDoubleType(eDoubleTypeXY);
2271             if ( param->supportsDefaultCoordinateSystem() ) {
2272                 param->setDefaultCoordinateSystem(eCoordinatesNormalised); // no need of kParamDefaultsNormalised
2273             } else {
2274                 gHostSupportsDefaultCoordinateSystem = false; // no multithread here, see kParamDefaultsNormalised
2275             }
2276             param->setDefault(1., 1.);
2277             param->setRange(0., 0., DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
2278             param->setDisplayRange(0, 0, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
2279             param->setIncrement(1.);
2280             param->setDimensionLabels(kParamRectangleInteractSizeDim1, kParamRectangleInteractSizeDim2);
2281             param->setHint("Width and height of the size rectangle.");
2282             param->setIncrement(1.);
2283             param->setDigits(0);
2284             if (page) {
2285                 page->addChild(*param);
2286             }
2287         }
2288 
2289     }
2290 
2291     Transform3x3DescribeInContextEnd(desc, context, page, false, Transform3x3Plugin::eTransform3x3ParamsTypeMotionBlur);
2292 
2293     {
2294         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamSrcClipChanged);
2295         param->setDefault(false);
2296         param->setIsSecretAndDisabled(true);
2297         param->setAnimates(false);
2298         param->setEvaluateOnChange(false);
2299         if (page) {
2300             page->addChild(*param);
2301         }
2302     }
2303 
2304     // interactive
2305     {
2306         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamTransformInteractive);
2307         param->setLabel(kParamTransformInteractiveLabel);
2308         param->setHint(kParamTransformInteractiveHint);
2309         param->setEvaluateOnChange(false);
2310         if (page) {
2311             page->addChild(*param);
2312         }
2313     }
2314 }
2315 
2316 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)2317 Card3DPluginFactory::createInstance(OfxImageEffectHandle handle,
2318                                        ContextEnum /*context*/)
2319 {
2320     return new Card3DPlugin(handle);
2321 }
2322 
2323 
2324 static Card3DPluginFactory p1(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
2325 mRegisterPluginFactoryInstance(p1)
2326 
2327 OFXS_NAMESPACE_ANONYMOUS_EXIT
2328