1
2
3 // TnzBase includes
4 #include "tfxattributes.h"
5 #include "tfxutil.h"
6 #include "tmacrofx.h"
7 #include "toutputproperties.h"
8 #include "tparamcontainer.h"
9
10 // TnzLib includes
11 #include "toonz/txsheet.h"
12 #include "toonz/tstageobjecttree.h"
13 #include "toonz/tcolumnfx.h"
14 #include "toonz/tcolumnfxset.h"
15 #include "toonz/fxdag.h"
16 #include "toonz/txshchildlevel.h"
17 #include "toonz/txshcell.h"
18 #include "toonz/txshleveltypes.h"
19 #include "toonz/txshlevelcolumn.h"
20 #include "toonz/txshpalettecolumn.h"
21 #include "toonz/txshzeraryfxcolumn.h"
22 #include "toonz/txshsimplelevel.h"
23 #include "toonz/dpiscale.h"
24 #include "toonz/tcamera.h"
25 #include "toonz/toonzscene.h"
26 #include "toonz/sceneproperties.h"
27 #include "toonz/plasticdeformerfx.h"
28 #include "toonz/stage.h"
29 #include "toonz/preferences.h"
30 #include "ttzpimagefx.h"
31 #include "toonz/txshsoundtextcolumn.h"
32 #include "toonz/txshsoundtextlevel.h"
33
34 #include "../stdfx/motionawarebasefx.h"
35 #include "../stdfx/textawarebasefx.h"
36
37 #include "toonz/scenefx.h"
38
39 /*
40 TODO: Some parts of the following render-tree building procedure should be
41 revised. In particular,
42 there is scarce support for frame-shifting fxs, whenever the frame-shift
43 can be resolved
44 only during rendering (as is the case for ParticlesFx).
45 */
46
47 //***************************************************************************************************
48 // TimeShuffleFx definition
49 //***************************************************************************************************
50
51 //! TimeShuffleFx is the rendering-tree equivalent of a sub-xsheet column.
52 /*!
53 TimeShuffleFx is a special-purpose fx which is used in render-tree building
54 procedures
55 to simulate the effect of a sub-xsheet.
56 \n\n
57 A rendering tree is a fully expanded tree that mixes implicit xsheet nesting
58 with
59 the explicit fxs dag <I> for a specific frame <\I>. Since the frame the tree
60 is developed from
61 is fixed, a sub-xsheet can be seen as a <I> frame setter <\I> fx.
62 */
63
64 class TimeShuffleFx final : public TRasterFx {
65 FX_DECLARATION(TimeShuffleFx)
66
67 private:
68 int m_frame; //!< Frame this fx redirects to
69 TFxTimeRegion m_timeRegion; //!< Input (outer) valid column frame range
70 TRasterFxPort m_port; //!< Input port
71 TXshCellColumn *m_cellColumn;
72
73 public:
TimeShuffleFx()74 TimeShuffleFx()
75 : TRasterFx(), m_frame(0), m_timeRegion(), m_cellColumn(nullptr) {
76 addInputPort("source", m_port);
77 }
~TimeShuffleFx()78 ~TimeShuffleFx() {}
79
clone(bool recursive=true) const80 TFx *clone(bool recursive = true) const override {
81 TimeShuffleFx *fx = dynamic_cast<TimeShuffleFx *>(TFx::clone(recursive));
82 assert(fx);
83
84 fx->setFrame(m_frame);
85 fx->setTimeRegion(getTimeRegion());
86 fx->setCellColumn(m_cellColumn);
87
88 return fx;
89 }
90
getFrame() const91 int getFrame() const { return m_frame; }
setFrame(int frame)92 void setFrame(int frame) { m_frame = frame; }
93
setTimeRegion(const TFxTimeRegion & timeRegion)94 void setTimeRegion(const TFxTimeRegion &timeRegion) {
95 m_timeRegion = timeRegion;
96 }
getTimeRegion() const97 TFxTimeRegion getTimeRegion() const override { return m_timeRegion; }
98
setCellColumn(TXshCellColumn * cellColumn)99 void setCellColumn(TXshCellColumn *cellColumn) { m_cellColumn = cellColumn; }
100
canHandle(const TRenderSettings & info,double frame)101 bool canHandle(const TRenderSettings &info, double frame) override {
102 return true;
103 }
104
getPluginId() const105 std::string getPluginId() const override { return std::string(); }
106
getLevelFrame(int frame) const107 int getLevelFrame(int frame) const {
108 if (!m_cellColumn) return m_frame;
109 TXshCell cell = m_cellColumn->getCell(tfloor(frame));
110 assert(!cell.isEmpty());
111 return cell.m_frameId.getNumber() - 1;
112 }
113
doCompute(TTile & tile,double frame,const TRenderSettings & ri)114 void doCompute(TTile &tile, double frame,
115 const TRenderSettings &ri) override {
116 if (!m_port.isConnected()) {
117 tile.getRaster()->clear();
118 return;
119 }
120
121 // Exchange frame with the stored one
122 TRasterFxP(m_port.getFx())->compute(tile, getLevelFrame(frame), ri);
123 }
124
doGetBBox(double frame,TRectD & bbox,const TRenderSettings & info)125 bool doGetBBox(double frame, TRectD &bbox,
126 const TRenderSettings &info) override {
127 if (!m_port.isConnected()) return false;
128 return TRasterFxP(m_port.getFx())
129 ->doGetBBox(getLevelFrame(frame), bbox, info);
130 }
131
getAlias(double frame,const TRenderSettings & info) const132 std::string getAlias(double frame,
133 const TRenderSettings &info) const override {
134 return TRasterFx::getAlias(getLevelFrame(frame), info);
135 }
136
doDryCompute(TRectD & rect,double frame,const TRenderSettings & info)137 void doDryCompute(TRectD &rect, double frame,
138 const TRenderSettings &info) override {
139 if (m_port.isConnected())
140 TRasterFxP(m_port.getFx())->dryCompute(rect, getLevelFrame(frame), info);
141 }
142
143 private:
144 // not implemented
145 TimeShuffleFx(const TimeShuffleFx &);
146 TimeShuffleFx &operator=(const TimeShuffleFx &);
147 };
148
149 FX_IDENTIFIER_IS_HIDDEN(TimeShuffleFx, "timeShuffleFx")
150
151 //***************************************************************************************************
152 // AffineFx definition
153 //***************************************************************************************************
154
155 //! AffineFx is a specialization of TGeometryFx which implements animated or
156 //! stage-controlled affines
157 /*!
158 This specific implementation of TGeometryFx is needed to deal with those
159 affines which are best
160 \b not resolved during the rendering-tree expansion procedure.
161 */
162
163 class AffineFx final : public TGeometryFx {
164 FX_DECLARATION(AffineFx)
165
166 private:
167 TXsheet *m_xsheet; //!< Xsheet owning m_stageObject
168 TStageObject *m_stageObject; //!< The stage object this AffineFx refers to
169 TRasterFxPort m_input; //!< The input port
170
171 public:
AffineFx()172 AffineFx() : m_xsheet(0), m_stageObject(0) {
173 addInputPort("source", m_input);
174 setName(L"AffineFx");
175 }
AffineFx(TXsheet * xsh,TStageObject * pegbar)176 AffineFx(TXsheet *xsh, TStageObject *pegbar)
177 : m_xsheet(xsh), m_stageObject(pegbar) {
178 addInputPort("source", m_input);
179 setName(L"AffineFx");
180 }
~AffineFx()181 ~AffineFx() {}
182
clone(bool recursive=true) const183 TFx *clone(bool recursive = true) const override {
184 AffineFx *fx = dynamic_cast<AffineFx *>(TFx::clone(recursive));
185 assert(fx);
186 fx->m_stageObject = m_stageObject;
187 fx->m_xsheet = m_xsheet;
188 return fx;
189 }
190
canHandle(const TRenderSettings & info,double frame)191 bool canHandle(const TRenderSettings &info, double frame) override {
192 return true;
193 }
194
getPlacement(double frame)195 TAffine getPlacement(double frame) override {
196 TAffine objAff = m_stageObject->getPlacement(frame);
197
198 double objZ = m_stageObject->getZ(frame);
199 double objNoScaleZ = m_stageObject->getGlobalNoScaleZ();
200
201 TStageObjectId cameraId =
202 m_xsheet->getStageObjectTree()->getCurrentCameraId();
203 TStageObject *camera = m_xsheet->getStageObject(cameraId);
204 TAffine cameraAff = camera->getPlacement(frame);
205 double cameraZ = camera->getZ(frame);
206
207 TAffine aff;
208 bool isVisible = TStageObject::perspective(aff, cameraAff, cameraZ, objAff,
209 objZ, objNoScaleZ);
210
211 if (!isVisible)
212 return TAffine(); // uh oh
213 else
214 return aff;
215 }
216
getParentPlacement(double frame)217 TAffine getParentPlacement(double frame) override {
218 return m_stageObject->getPlacement(frame);
219 }
220
getPluginId() const221 std::string getPluginId() const override { return std::string(); }
222
223 private:
224 // not implemented
225 AffineFx(const AffineFx &);
226 AffineFx &operator=(const AffineFx &);
227 };
228
229 FX_IDENTIFIER_IS_HIDDEN(AffineFx, "affineFx")
230
231 //***************************************************************************************************
232 // PlacedFx definition
233 //***************************************************************************************************
234
235 //! PlacedFx is the enriched form of a TRasterFx during render-tree building.
236 class PlacedFx {
237 public:
238 double m_z; //!< Z value for this fx's column
239 double m_so; //!< Same as above, for stacking order
240 int m_columnIndex; //!< This fx's column index
241
242 TFxP m_fx; //!< The referenced fx
243 TAffine m_aff; //!<
244
245 TFxPort *m_leftXsheetPort;
246
247 public:
PlacedFx()248 PlacedFx()
249 : m_z(0)
250 , m_so(0)
251 , m_columnIndex(-1)
252 , m_fx(0)
253 , m_aff()
254 , m_leftXsheetPort(0) {}
PlacedFx(const TFxP & fx)255 explicit PlacedFx(const TFxP &fx)
256 : m_z(0)
257 , m_so(0)
258 , m_columnIndex(-1)
259 , m_fx(fx)
260 , m_aff()
261 , m_leftXsheetPort(0) {}
262
operator <(const PlacedFx & pf) const263 bool operator<(const PlacedFx &pf) const {
264 return (m_z < pf.m_z)
265 ? true
266 : (m_z > pf.m_z)
267 ? false
268 : (m_so < pf.m_so)
269 ? true
270 : (m_so > pf.m_so)
271 ? false
272 : (m_columnIndex < pf.m_columnIndex);
273 }
274
makeFx()275 TFxP makeFx() {
276 return (!m_fx)
277 ? TFxP()
278 : (m_aff == TAffine()) ? m_fx : TFxUtil::makeAffine(m_fx, m_aff);
279 }
280 };
281
282 //***************************************************************************************************
283 // Local namespace
284 //***************************************************************************************************
285
286 namespace {
287
timeShuffle(TFxP fx,int frame,TFxTimeRegion timeRegion,TXshCellColumn * cellColumn)288 TFxP timeShuffle(TFxP fx, int frame, TFxTimeRegion timeRegion,
289 TXshCellColumn *cellColumn) {
290 TimeShuffleFx *timeShuffle = new TimeShuffleFx();
291
292 timeShuffle->setFrame(frame);
293 timeShuffle->setTimeRegion(timeRegion);
294 timeShuffle->setCellColumn(cellColumn);
295 if (!timeShuffle->connect("source", fx.getPointer()))
296 assert(!"Could not connect ports!");
297
298 return timeShuffle;
299 };
300
301 } // namespace
302
303 //***************************************************************************************************
304 // Column-related functions
305 //***************************************************************************************************
306
getColumnPlacement(TAffine & aff,TXsheet * xsh,double row,int col,bool isPreview)307 bool getColumnPlacement(TAffine &aff, TXsheet *xsh, double row, int col,
308 bool isPreview) {
309 if (col < 0) return false;
310 TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(col));
311 TAffine objAff = pegbar->getPlacement(row);
312 double objZ = pegbar->getZ(row);
313 double noScaleZ = pegbar->getGlobalNoScaleZ();
314
315 TStageObjectId cameraId;
316 if (isPreview)
317 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
318 else
319 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
320 TStageObject *camera = xsh->getStageObject(cameraId);
321 TAffine cameraAff = camera->getPlacement(row);
322 double cameraZ = camera->getZ(row);
323
324 bool isVisible = TStageObject::perspective(aff, cameraAff, cameraZ, objAff,
325 objZ, noScaleZ);
326
327 return isVisible;
328 }
329
330 //-------------------------------------------------------------------
331
getColumnPlacement(PlacedFx & pf,TXsheet * xsh,double row,int col,bool isPreview)332 static bool getColumnPlacement(PlacedFx &pf, TXsheet *xsh, double row, int col,
333 bool isPreview) {
334 if (col < 0) return false;
335 TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(col));
336 TAffine objAff = pegbar->getPlacement(row);
337 pf.m_z = pegbar->getZ(row);
338 pf.m_so = pegbar->getSO(row);
339
340 TStageObjectId cameraId;
341 if (isPreview)
342 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
343 else
344 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
345 TStageObject *camera = xsh->getStageObject(cameraId);
346 TAffine cameraAff = camera->getPlacement(row);
347 double cameraZ = camera->getZ(row);
348
349 bool isVisible =
350 TStageObject::perspective(pf.m_aff, cameraAff, cameraZ, objAff, pf.m_z,
351 pegbar->getGlobalNoScaleZ());
352
353 return isVisible;
354 }
355
356 //-------------------------------------------------------------------
357 /*-- Objectの位置を得る --*/
getStageObjectPlacement(TAffine & aff,TXsheet * xsh,double row,TStageObjectId & id,bool isPreview)358 static bool getStageObjectPlacement(TAffine &aff, TXsheet *xsh, double row,
359 TStageObjectId &id, bool isPreview) {
360 TStageObject *pegbar = xsh->getStageObjectTree()->getStageObject(id, false);
361 if (!pegbar) return false;
362
363 TAffine objAff = pegbar->getPlacement(row);
364 double objZ = pegbar->getZ(row);
365 double noScaleZ = pegbar->getGlobalNoScaleZ();
366
367 TStageObjectId cameraId;
368 if (isPreview)
369 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
370 else
371 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
372 TStageObject *camera = xsh->getStageObject(cameraId);
373 TAffine cameraAff = camera->getPlacement(row);
374 double cameraZ = camera->getZ(row);
375
376 bool isVisible = TStageObject::perspective(aff, cameraAff, cameraZ, objAff,
377 objZ, noScaleZ);
378
379 return isVisible;
380 }
381
382 /*-- typeとindexからStageObjectIdを得る --*/
383 namespace {
getMotionObjectId(MotionObjectType type,int index)384 TStageObjectId getMotionObjectId(MotionObjectType type, int index) {
385 switch (type) {
386 case OBJTYPE_OWN:
387 return TStageObjectId::NoneId;
388 break;
389 case OBJTYPE_COLUMN:
390 if (index == 0) return TStageObjectId::NoneId;
391 return TStageObjectId::ColumnId(index - 1);
392 break;
393 case OBJTYPE_PEGBAR:
394 if (index == 0) return TStageObjectId::NoneId;
395 return TStageObjectId::PegbarId(index - 1);
396 break;
397 case OBJTYPE_TABLE:
398 return TStageObjectId::TableId;
399 break;
400 case OBJTYPE_CAMERA:
401 if (index == 0) return TStageObjectId::NoneId;
402 return TStageObjectId::CameraId(index - 1);
403 break;
404 }
405
406 return TStageObjectId::NoneId;
407 }
408 }; // namespace
409
410 //-------------------------------------------------------------------
411
getColumnSpeed(TXsheet * xsh,double row,int col,bool isPreview)412 static TPointD getColumnSpeed(TXsheet *xsh, double row, int col,
413 bool isPreview) {
414 TAffine aff;
415 TPointD a, b;
416 const double h = 0.001;
417 getColumnPlacement(aff, xsh, row + h, col, isPreview);
418
419 /*-- カラムと、カメラの動きに反応 --*/
420 TStageObjectId cameraId;
421 if (isPreview)
422 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
423 else
424 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
425 TStageObject *camera = xsh->getStageObject(cameraId);
426 TAffine cameraAff = camera->getPlacement(row + h);
427 a = aff * TPointD(-cameraAff.a13, -cameraAff.a23);
428
429 aff = TAffine();
430 getColumnPlacement(aff, xsh, row - h, col, isPreview);
431
432 cameraAff = camera->getPlacement(row - h);
433 b = aff * TPointD(-cameraAff.a13, -cameraAff.a23);
434
435 return (b - a) * (0.5 / h);
436 }
437
438 //-------------------------------------------------------------------
439 /*-- オブジェクトの軌跡を、基準点との差分で得る
440 objectId: 移動の参考にするオブジェクト。自分自身の場合はNoneId
441 --*/
getColumnMotionPoints(TXsheet * xsh,double row,int col,TStageObjectId & objectId,bool isPreview,double shutterStart,double shutterEnd,int traceResolution)442 static QList<TPointD> getColumnMotionPoints(TXsheet *xsh, double row, int col,
443 TStageObjectId &objectId,
444 bool isPreview, double shutterStart,
445 double shutterEnd,
446 int traceResolution) {
447 /*-- 前後フレームが共に0なら空のリストを返す --*/
448 if (shutterStart == 0.0 && shutterEnd == 0.0) return QList<TPointD>();
449
450 /*-- 現在のカメラを得る --*/
451 TStageObjectId cameraId;
452 if (isPreview)
453 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
454 else
455 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
456 TStageObject *camera = xsh->getStageObject(cameraId);
457 TAffine dpiAff = getDpiAffine(camera->getCamera());
458
459 /*-- 基準点の位置を得る --*/
460 TAffine aff;
461
462 /*-- objectIdが有効なものかどうかチェック --*/
463 bool useOwnMotion = false;
464 if (objectId == TStageObjectId::NoneId ||
465 !xsh->getStageObjectTree()->getStageObject(objectId, false)) {
466 getColumnPlacement(aff, xsh, row, col, isPreview);
467 useOwnMotion = true;
468 } else {
469 getStageObjectPlacement(aff, xsh, row, objectId, isPreview);
470 }
471
472 TAffine cameraAff = camera->getPlacement(row);
473 TPointD basePos =
474 dpiAff.inv() * aff * TPointD(-cameraAff.a13, -cameraAff.a23);
475
476 /*-- 結果を収めるリスト --*/
477 QList<TPointD> points;
478 /*-- 軌跡点間のフレーム間隔 --*/
479 double dFrame = (shutterStart + shutterEnd) / (double)traceResolution;
480 /*-- 各点の位置を、基準点との差分で格納していく --*/
481 for (int i = 0; i <= traceResolution; i++) {
482 /*-- 基準位置とのフレーム差 --*/
483 double frameOffset = -shutterStart + dFrame * (double)i;
484 /*-- 基準位置とのフレーム差が無ければ、基準点に一致するので差分は0を入れる
485 * --*/
486 if (frameOffset == 0.0) {
487 points.append(TPointD(0.0, 0.0));
488 continue;
489 }
490
491 double targetFrame = row + frameOffset;
492 // Proper position cannot be obtained for frame = -1.0
493 if (targetFrame == -1.0) targetFrame = -0.9999;
494
495 /*-- 自分自身の動きを使うか、別オブジェクトの動きを使うか --*/
496 if (useOwnMotion)
497 getColumnPlacement(aff, xsh, targetFrame, col, isPreview);
498 else
499 getStageObjectPlacement(aff, xsh, targetFrame, objectId, isPreview);
500
501 TAffine cameraAff = camera->getPlacement(targetFrame);
502 TPointD tmpPos =
503 dpiAff.inv() * aff * TPointD(-cameraAff.a13, -cameraAff.a23);
504
505 /*-- 基準位置との差を記録 --*/
506 points.append(tmpPos - basePos);
507 }
508 return points;
509 }
510
511 namespace {
512
getNoteText(TXsheet * xsh,double row,int col,int noteColumnIndex,bool neighbor)513 QString getNoteText(TXsheet *xsh, double row, int col, int noteColumnIndex,
514 bool neighbor) {
515 int colIndex;
516 if (neighbor)
517 colIndex = col - 1;
518 else
519 colIndex = noteColumnIndex;
520
521 TXshColumn *column = xsh->getColumn(colIndex);
522 if (!column || !column->getSoundTextColumn()) return QString();
523
524 TXshCell cell = xsh->getCell(row, colIndex);
525 if (cell.isEmpty() || !cell.getSoundTextLevel()) return QString();
526
527 return cell.getSoundTextLevel()->getFrameText(cell.m_frameId.getNumber() - 1);
528 }
529 }; // namespace
530
531 //***************************************************************************************************
532 // FxBuilder definition
533 //***************************************************************************************************
534
535 class FxBuilder {
536 public:
537 ToonzScene *m_scene;
538 TXsheet *m_xsh;
539 TAffine m_cameraAff;
540 double m_cameraZ;
541 double m_frame;
542 int m_whichLevels;
543 bool m_isPreview;
544 bool m_expandXSheet;
545
546 // in the makePF() methods m_particleDescendentCount>0 iff the TFx* is an
547 // ancestor
548 // (at least) of a particle Fx
549 int m_particleDescendentCount;
550
551 public:
552 FxBuilder(ToonzScene *scene, TXsheet *xsh, double frame, int whichLevels,
553 bool isPreview = false, bool expandXSheet = true);
554
555 TFxP buildFx();
556 TFxP buildFx(const TFxP &root, BSFX_Transforms_Enum transforms);
557
558 PlacedFx makePF(TLevelColumnFx *fx);
559 PlacedFx makePF(TPaletteColumnFx *fx);
560 PlacedFx makePF(TZeraryColumnFx *fx);
561 PlacedFx makePF(TXsheetFx *fx);
562 PlacedFx makePFfromUnaryFx(TFx *fx);
563 PlacedFx makePFfromGenericFx(TFx *fx);
564 PlacedFx makePF(TFx *fx);
565
566 TFxP getFxWithColumnMovements(const PlacedFx &pf);
567
568 bool addPlasticDeformerFx(PlacedFx &pf);
569 };
570
571 //===================================================================
572
FxBuilder(ToonzScene * scene,TXsheet * xsh,double frame,int whichLevels,bool isPreview,bool expandXSheet)573 FxBuilder::FxBuilder(ToonzScene *scene, TXsheet *xsh, double frame,
574 int whichLevels, bool isPreview, bool expandXSheet)
575 : m_scene(scene)
576 , m_xsh(xsh)
577 , m_frame(frame)
578 , m_whichLevels(whichLevels)
579 , m_isPreview(isPreview)
580 , m_expandXSheet(expandXSheet)
581 , m_particleDescendentCount(0) {
582 TStageObjectId cameraId;
583 if (m_isPreview)
584 cameraId = m_xsh->getStageObjectTree()->getCurrentPreviewCameraId();
585 else
586 cameraId = m_xsh->getStageObjectTree()->getCurrentCameraId();
587
588 TStageObject *camera = m_xsh->getStageObject(cameraId);
589 m_cameraAff = camera->getPlacement(m_frame);
590 m_cameraZ = camera->getZ(m_frame);
591 }
592
593 //-------------------------------------------------------------------
594
buildFx()595 TFxP FxBuilder::buildFx() {
596 TFx *outputFx = m_xsh->getFxDag()->getOutputFx(0);
597 if (!outputFx || outputFx->getInputPortCount() != 1 ||
598 outputFx->getInputPort(0)->getFx() == 0)
599 return TFxP();
600
601 outputFx->setName(L"OutputFx");
602
603 assert(m_particleDescendentCount == 0);
604 PlacedFx pf = makePF(outputFx->getInputPort(0)->getFx());
605 assert(m_particleDescendentCount == 0);
606
607 TAffine cameraFullAff = m_cameraAff * TScale((1000 + m_cameraZ) / 1000);
608 return TFxUtil::makeAffine(pf.makeFx(), cameraFullAff.inv());
609 }
610
611 //-------------------------------------------------------------------
612
buildFx(const TFxP & root,BSFX_Transforms_Enum transforms)613 TFxP FxBuilder::buildFx(const TFxP &root, BSFX_Transforms_Enum transforms) {
614 assert(m_particleDescendentCount == 0);
615 PlacedFx pf = makePF(root.getPointer());
616 assert(m_particleDescendentCount == 0);
617
618 TFxP fx = (transforms & BSFX_COLUMN_TR) ? pf.makeFx() : pf.m_fx;
619 if (transforms & BSFX_CAMERA_TR) {
620 TAffine cameraFullAff = m_cameraAff * TScale((1000 + m_cameraZ) / 1000);
621 fx = TFxUtil::makeAffine(fx, cameraFullAff.inv());
622 }
623
624 return fx;
625 }
626
627 //-------------------------------------------------------------------
628
getFxWithColumnMovements(const PlacedFx & pf)629 TFxP FxBuilder::getFxWithColumnMovements(const PlacedFx &pf) {
630 TFxP fx = pf.m_fx;
631 if (!fx) return fx;
632
633 if (pf.m_columnIndex == -1) return pf.m_fx;
634
635 TStageObjectId id = TStageObjectId::ColumnId(pf.m_columnIndex);
636 TStageObject *pegbar = m_xsh->getStageObject(id);
637
638 AffineFx *affFx = new AffineFx(m_xsh, pegbar);
639 affFx->getInputPort(0)->setFx(fx.getPointer());
640
641 return affFx;
642 }
643
644 //-------------------------------------------------------------------
645
addPlasticDeformerFx(PlacedFx & pf)646 bool FxBuilder::addPlasticDeformerFx(PlacedFx &pf) {
647 TStageObject *obj =
648 m_xsh->getStageObject(TStageObjectId::ColumnId(pf.m_columnIndex));
649 TStageObjectId parentId(obj->getParent());
650
651 if (parentId.isColumn() && obj->getParentHandle()[0] != 'H') {
652 const SkDP &sd =
653 m_xsh->getStageObject(parentId)->getPlasticSkeletonDeformation();
654
655 const TXshCell &parentCell = m_xsh->getCell(m_frame, parentId.getIndex());
656 TXshSimpleLevel *parentSl = parentCell.getSimpleLevel();
657
658 if (sd && parentSl && (parentSl->getType() == MESH_XSHLEVEL)) {
659 // Plastic Deformer case - add the corresponding fx,
660 // absorb the dpi and local column placement affines
661
662 PlasticDeformerFx *plasticFx = new PlasticDeformerFx;
663 plasticFx->m_xsh = m_xsh;
664 plasticFx->m_col = parentId.getIndex();
665 plasticFx->m_texPlacement = obj->getLocalPlacement(m_frame);
666
667 if (!plasticFx->connect("source", pf.m_fx.getPointer()))
668 assert(!"Could not connect ports!");
669
670 pf.m_fx = plasticFx;
671 pf.m_aff = pf.m_aff * plasticFx->m_texPlacement.inv();
672
673 return true;
674 }
675 }
676
677 return false;
678 }
679
680 //-------------------------------------------------------------------
681
makePF(TFx * fx)682 PlacedFx FxBuilder::makePF(TFx *fx) {
683 if (!fx) return PlacedFx();
684
685 if (TLevelColumnFx *lcfx = dynamic_cast<TLevelColumnFx *>(fx))
686 return makePF(lcfx);
687 else if (TPaletteColumnFx *pcfx = dynamic_cast<TPaletteColumnFx *>(fx))
688 return makePF(pcfx);
689 else if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx))
690 return makePF(zcfx);
691 else if (TXsheetFx *xsfx = dynamic_cast<TXsheetFx *>(fx))
692 return makePF(xsfx);
693 else if (fx->getInputPortCount() == 1)
694 return makePFfromUnaryFx(fx);
695 else
696 return makePFfromGenericFx(fx);
697 }
698
699 //-------------------------------------------------------------------
700
makePF(TXsheetFx * fx)701 PlacedFx FxBuilder::makePF(TXsheetFx *fx) {
702 if (!m_expandXSheet) // Xsheet expansion is typically blocked for render-tree
703 // building of
704 return PlacedFx(fx); // post-xsheet fxs only.
705
706 // Expand the render-tree from terminal fxs
707 TFxSet *fxs = m_xsh->getFxDag()->getTerminalFxs();
708 int m = fxs->getFxCount();
709 if (m == 0) return PlacedFx();
710
711 std::vector<PlacedFx> pfs(m);
712 int i;
713 for (i = 0; i < m; i++) {
714 // Expand each terminal fx
715 TFx *fx = fxs->getFx(i);
716 assert(fx);
717 pfs[i] = makePF(fx); // Builds the sub-render-trees here
718 }
719
720 /*--
721 * Xsheetに複数ノードが繋がっていた場合、PlacedFxの条件に従ってOverノードの付く順番を決める
722 * --*/
723 std::sort(pfs.begin(),
724 pfs.end()); // Sort each terminal depending on Z/SO/Column index
725
726 // Compose them in a cascade of overs (or affines 'leftXsheetPort' cases)
727 TFxP currentFx =
728 pfs[0].makeFx(); // Adds an NaAffineFx if pf.m_aff is not the identity
729 for (i = 1; i < m; i++) {
730 TFxP fx = pfs[i].makeFx(); // See above
731 if (pfs[i].m_leftXsheetPort) {
732 // LeftXsheetPort cases happen for those fxs like Add, Multiply, etc that
733 // declare an xsheet-like input port.
734 // That is, all terminal fxs below ours are attached COMPOSED to enter the
735 // fx's leftXsheet input port.
736
737 TFxP inputFx = currentFx;
738 inputFx = TFxUtil::makeAffine(inputFx, pfs[i].m_aff.inv());
739 pfs[i].m_leftXsheetPort->setFx(inputFx.getPointer());
740 currentFx = fx;
741 } else {
742 if (Preferences::instance()
743 ->isShowRasterImagesDarkenBlendedInViewerEnabled())
744 currentFx = TFxUtil::makeDarken(currentFx, fx);
745 else
746 currentFx = TFxUtil::makeOver(currentFx, fx); // Common over case
747 }
748 }
749
750 return PlacedFx(currentFx);
751 }
752
753 //-------------------------------------------------------------------
754
755 //! Creates and returns a PlacedFx for a TLevelColumnFx.
756 /*
757 Fxs under a ParticlesFx node seem to have special treatment - that is,
758 empty column cells are still attached to a not-empty PlacedFx.
759
760 This must be a remnant of old Toonz code, that should no longer remain here -
761 in fact, well, you can only extract an empty render from an empty column!
762 So why bother?
763 */
makePF(TLevelColumnFx * lcfx)764 PlacedFx FxBuilder::makePF(TLevelColumnFx *lcfx) {
765 assert(m_scene);
766 assert(lcfx);
767 assert(lcfx->getColumn());
768 if (!lcfx || !lcfx->getColumn()) return PlacedFx();
769
770 if (!lcfx->getColumn()->isPreviewVisible()) // This is the 'eye' icon
771 // property in the column header
772 // interface
773 return PlacedFx(); // that disables rendering of this particular column
774
775 // Retrieve the corresponding xsheet cell to build up
776 /*-- 現在のフレームのセルを取得 --*/
777 TXshCell cell = lcfx->getColumn()->getCell(tfloor(m_frame));
778 int levelFrame = cell.m_frameId.getNumber() - 1;
779
780 /*-- ParticlesFxに繋がっておらず、空セルの場合は 中身無しを返す --*/
781 if (m_particleDescendentCount == 0 && cell.isEmpty()) return PlacedFx();
782
783 if (m_whichLevels == TOutputProperties::AnimatedOnly) {
784 // In case only 'animated levels' are selected to be rendered, exclude all
785 // 'backgrounds' - that is,
786 // fullcolor levels...
787
788 // Still, I wonder if this is still used in Toonz. I don't remember seeing
789 // it anywhere :\ ?
790
791 TXshLevel *xl = cell.m_level.getPointer();
792
793 /*-- ParticleFxのTextureポートに繋がっていない場合 --*/
794 if (m_particleDescendentCount == 0) {
795 if (!xl ||
796 (xl->getType() != PLI_XSHLEVEL && xl->getType() != TZP_XSHLEVEL &&
797 xl->getType() != CHILD_XSHLEVEL))
798 return PlacedFx();
799 }
800 /*-- ParticleFxのTextureポートに繋がっている場合 --*/
801 else {
802 if (xl && xl->getType() != PLI_XSHLEVEL &&
803 xl->getType() != TZP_XSHLEVEL && xl->getType() != CHILD_XSHLEVEL)
804 return PlacedFx();
805 }
806 }
807
808 // Build a PlacedFx for the column - start with the standard version for
809 // common (image) levels
810 PlacedFx pf;
811 pf.m_columnIndex = lcfx->getColumn()->getIndex();
812 pf.m_fx = lcfx;
813
814 // Build column placement
815 bool columnVisible =
816 getColumnPlacement(pf, m_xsh, m_frame, pf.m_columnIndex, m_isPreview);
817
818 /*-- subXsheetのとき、その中身もBuildFxを実行 --*/
819 if (!cell.isEmpty() && cell.m_level->getChildLevel()) {
820 // Treat the sub-xsheet case - build the sub-render-tree and reassign stuff
821 // to pf
822 TXsheet *xsh = cell.m_level->getChildLevel()->getXsheet();
823
824 // Build the sub-render-tree
825 FxBuilder builder(m_scene, xsh, levelFrame, m_whichLevels, m_isPreview);
826
827 // Then, add the TimeShuffleFx
828 pf.m_fx = timeShuffle(builder.buildFx(), levelFrame, lcfx->getTimeRegion(),
829 lcfx->getColumn());
830 pf.m_fx->setIdentifier(lcfx->getIdentifier());
831 pf.m_fx->getAttributes()->passiveCacheDataIdx() =
832 lcfx->getAttributes()->passiveCacheDataIdx();
833
834 // If the level should sustain a Plastic deformation, add the corresponding
835 // fx
836 addPlasticDeformerFx(pf);
837 }
838
839 if (columnVisible) {
840 // Column is visible, alright
841 TXshSimpleLevel *sl = cell.isEmpty() ? 0 : cell.m_level->getSimpleLevel();
842 if (sl) {
843 // If the level should sustain a Plastic deformation, add the
844 // corresponding fx
845 if (!addPlasticDeformerFx(pf)) {
846 // Common (image) level case - add an NaAffineFx to compensate for the
847 // image's dpi
848 TAffine dpiAff = ::getDpiAffine(
849 sl, cell.m_frameId, true); // true stands for 'force full-sampling'
850 pf.m_fx = TFxUtil::makeAffine(pf.m_fx, dpiAff);
851 if (pf.m_fx) pf.m_fx->setName(L"LevelColumn AffineFx");
852 }
853 } else {
854 // Okay, weird code ensues. This is what happens on non-common image
855 // cases, which should be:
856
857 // 1. Sub-Xsheet cases - and it really shouldn't
858 // 2. Empty cell cases - with m_particles_blabla > 0; and again I don't
859 // get why on earth this should happen...
860
861 // Please, note that (1) is a bug, although it happens when inserting a
862 // common level and a sub-xsh
863 // level in the same column...
864
865 // when a cell not exists, there is no way to keep the dpi of the image!
866 // in this case it is kept the dpi of the first cell not empty in the
867 // column!
868 /*--
869 * 空セルのとき、Dpiアフィン変換には、その素材が入っている一番上のセルのものを使う
870 * --*/
871 TXshLevelColumn *column = lcfx->getColumn();
872 int i;
873 for (i = 0; i < column->getRowCount(); i++) {
874 TXshCell dpiCell = lcfx->getColumn()->getCell(i);
875 if (dpiCell.isEmpty()) continue;
876
877 sl = dpiCell.m_level->getSimpleLevel();
878 if (!sl) break;
879
880 TAffine dpiAff = ::getDpiAffine(sl, dpiCell.m_frameId, true);
881 pf.m_fx = TFxUtil::makeAffine(pf.m_fx, dpiAff);
882 break;
883 }
884 }
885
886 // Apply column's color filter and semi-transparency for rendering
887 TXshLevelColumn *column = lcfx->getColumn();
888 if (m_scene->getProperties()->isColumnColorFilterOnRenderEnabled() &&
889 (column->getFilterColorId() != TXshColumn::FilterNone ||
890 (column->isCamstandVisible() && column->getOpacity() != 255))) {
891 TPixel32 colorScale = column->getFilterColor();
892 colorScale.m = column->getOpacity();
893 pf.m_fx = TFxUtil::makeColumnColorFilter(pf.m_fx, colorScale);
894 }
895
896 return pf;
897 } else
898 return PlacedFx();
899 }
900
901 //-------------------------------------------------------------------
902
makePF(TPaletteColumnFx * pcfx)903 PlacedFx FxBuilder::makePF(TPaletteColumnFx *pcfx) {
904 assert(pcfx);
905 assert(pcfx->getColumn());
906 if (!pcfx->getColumn()->isPreviewVisible()) return PlacedFx();
907
908 TXshCell cell = pcfx->getColumn()->getCell(tfloor(m_frame));
909 if (cell.isEmpty()) return PlacedFx();
910
911 PlacedFx pf;
912 pf.m_columnIndex = pcfx->getColumn()->getIndex();
913 pf.m_fx = pcfx;
914
915 return pf;
916 }
917
918 //-------------------------------------------------------------------
919
makePF(TZeraryColumnFx * zcfx)920 PlacedFx FxBuilder::makePF(TZeraryColumnFx *zcfx) {
921 assert(zcfx);
922 assert(zcfx->getColumn());
923
924 if (!zcfx->getColumn()->isPreviewVisible()) // ...
925 return PlacedFx();
926
927 if (!zcfx->getAttributes()->isEnabled()) // ...
928 return PlacedFx();
929
930 TFx *fx = zcfx->getZeraryFx();
931 if (!fx || !fx->getAttributes()->isEnabled()) // ... Perhaps these shouldn't
932 // be tested altogether? Only 1
933 // truly works !
934 return PlacedFx();
935
936 TXshCell cell = zcfx->getColumn()->getCell(tfloor(m_frame));
937 if (cell.isEmpty()) return PlacedFx();
938
939 // Build
940 PlacedFx pf;
941 pf.m_columnIndex = zcfx->getColumn()->getIndex();
942 pf.m_fx =
943 fx->clone(false); // Detach the fx with a clone. Why? It's typically done
944 // to build fx connections in the render-tree freely.
945 // Here, it's used just for particles, I guess...
946 // Deal with input sub-trees
947 for (int i = 0; i < fx->getInputPortCount(); ++i) {
948 // Note that only particles should end up here, currently
949 if (TFxP inputFx = fx->getInputPort(i)->getFx()) {
950 PlacedFx inputPF;
951
952 // if the effect is a particle fx, it is necessary to consider also empty
953 // cells
954 // this causes a connection with the effect and a level also with empty
955 // cells.
956 if (fx->getFxType() == "STD_particlesFx" ||
957 fx->getFxType() == "STD_iwa_TiledParticlesFx" ||
958 fx->getFxType() == "STD_tiledParticlesFx") {
959 m_particleDescendentCount++;
960 inputPF = makePF(inputFx.getPointer());
961 m_particleDescendentCount--;
962 } else
963 inputPF = makePF(inputFx.getPointer());
964
965 inputFx = getFxWithColumnMovements(inputPF);
966 if (!inputFx) continue;
967
968 inputFx = TFxUtil::makeAffine(inputFx, pf.m_aff.inv());
969 if (!pf.m_fx->connect(pf.m_fx->getInputPortName(i), inputFx.getPointer()))
970 assert(!"Could not connect ports!");
971 }
972 }
973
974 if (pf.m_fx->getFxType() == "STD_iwa_TextFx") {
975 TextAwareBaseFx *textFx =
976 dynamic_cast<TextAwareBaseFx *>(pf.m_fx.getPointer());
977 if (textFx && textFx->getSourceType() != TextAwareBaseFx::INPUT_TEXT) {
978 int noteColumnIndex = textFx->getNoteColumnIndex();
979 bool getNeighbor =
980 (textFx->getSourceType() == TextAwareBaseFx::NEARBY_COLUMN);
981 textFx->setNoteLevelStr(getNoteText(m_xsh, m_frame, pf.m_columnIndex,
982 noteColumnIndex, getNeighbor));
983 }
984 }
985
986 // Add the column placement NaAffineFx
987 if (getColumnPlacement(pf, m_xsh, m_frame, pf.m_columnIndex, m_isPreview))
988 return pf;
989 else
990 return PlacedFx();
991 }
992
993 //-------------------------------------------------------------------
994
makePFfromUnaryFx(TFx * fx)995 PlacedFx FxBuilder::makePFfromUnaryFx(TFx *fx) {
996 assert(!dynamic_cast<TLevelColumnFx *>(fx));
997 assert(!dynamic_cast<TZeraryColumnFx *>(fx));
998 assert(fx->getInputPortCount() == 1);
999
1000 TFx *inputFx = fx->getInputPort(0)->getFx();
1001 if (!inputFx) return PlacedFx();
1002
1003 PlacedFx pf = makePF(inputFx); // Build sub-render-tree
1004 if (!pf.m_fx) return PlacedFx();
1005
1006 if (fx->getAttributes()->isEnabled()) {
1007 // Fx is enabled, so insert it in the render-tree
1008
1009 // Clone this fx necessary
1010 if (pf.m_fx.getPointer() != inputFx || // As in an earlier makePF, clone
1011 // whenever input connections have
1012 // changed
1013 fx->getAttributes()->isSpeedAware() || // In the 'speedAware' case,
1014 // we'll alter the fx's
1015 // attributes (see below)
1016 dynamic_cast<TMacroFx *>(fx)) // As for macros... I'm not sure. Not
1017 // even who wrote this *understood*
1018 // why - it just solved a bug X( . Investigate!
1019 {
1020 fx = fx->clone(false);
1021 if (!fx->connect(fx->getInputPortName(0), pf.m_fx.getPointer()))
1022 assert(!"Could not connect ports!");
1023 }
1024
1025 pf.m_fx = fx;
1026
1027 if (fx->getAttributes()->isSpeedAware()) {
1028 /*-- スピードでなく、軌跡を取得する場合 --*/
1029 MotionAwareBaseFx *mabfx = dynamic_cast<MotionAwareBaseFx *>(fx);
1030 if (mabfx) {
1031 double shutterStart = mabfx->getShutterStart()->getValue(m_frame);
1032 double shutterEnd = mabfx->getShutterEnd()->getValue(m_frame);
1033 int traceResolution = mabfx->getTraceResolution()->getValue();
1034 /*-- 移動の参考にするオブジェクトの取得。自分自身の場合はNoneId --*/
1035 MotionObjectType type = mabfx->getMotionObjectType();
1036 int index = mabfx->getMotionObjectIndex()->getValue();
1037 TStageObjectId objectId = getMotionObjectId(type, index);
1038 fx->getAttributes()->setMotionPoints(getColumnMotionPoints(
1039 m_xsh, m_frame, pf.m_columnIndex, objectId, m_isPreview,
1040 shutterStart, shutterEnd, traceResolution));
1041 } else {
1042 TPointD speed =
1043 getColumnSpeed(m_xsh, m_frame, pf.m_columnIndex, m_isPreview);
1044 fx->getAttributes()->setSpeed(speed);
1045 }
1046 }
1047 }
1048
1049 return pf;
1050 }
1051
1052 //-------------------------------------------------------------------
1053
makePFfromGenericFx(TFx * fx)1054 PlacedFx FxBuilder::makePFfromGenericFx(TFx *fx) {
1055 assert(!dynamic_cast<TLevelColumnFx *>(fx));
1056 assert(!dynamic_cast<TZeraryColumnFx *>(fx));
1057
1058 PlacedFx pf;
1059
1060 if (!fx->getAttributes()->isEnabled()) {
1061 if (fx->getInputPortCount() == 0) return PlacedFx();
1062
1063 TFxP inputFx = fx->getInputPort(fx->getPreferredInputPort())->getFx();
1064 if (inputFx) return makePF(inputFx.getPointer());
1065
1066 return pf;
1067 }
1068
1069 // Multi-input fxs are always cloned - since at least one of its input ports
1070 // will have an NaAffineFx
1071 // injected just before its actual input fx.
1072 pf.m_fx = fx->clone(false);
1073
1074 bool firstInput = true;
1075
1076 int m = fx->getInputPortCount();
1077 for (int i = 0; i < m; ++i) {
1078 if (TFxP inputFx = fx->getInputPort(i)->getFx()) {
1079 PlacedFx inputPF = makePF(inputFx.getPointer());
1080 inputFx = inputPF.m_fx;
1081 if (!inputFx) continue;
1082
1083 if (firstInput) {
1084 firstInput = false;
1085
1086 // The first found input PlacedFx carries its placement infos up
1087 pf.m_aff = inputPF.m_aff;
1088 pf.m_columnIndex = inputPF.m_columnIndex;
1089 pf.m_z = inputPF.m_z;
1090 pf.m_so = inputPF.m_so;
1091
1092 /*-- 軌跡を取得するBinaryFxの場合 --*/
1093 if (pf.m_fx->getAttributes()->isSpeedAware()) {
1094 MotionAwareBaseFx *mabfx =
1095 dynamic_cast<MotionAwareBaseFx *>(pf.m_fx.getPointer());
1096 if (mabfx) {
1097 double shutterStart = mabfx->getShutterStart()->getValue(m_frame);
1098 double shutterEnd = mabfx->getShutterEnd()->getValue(m_frame);
1099 int traceResolution = mabfx->getTraceResolution()->getValue();
1100 /*-- 移動の参考にするオブジェクトの取得。自分自身の場合はNoneId --*/
1101 MotionObjectType type = mabfx->getMotionObjectType();
1102 int index = mabfx->getMotionObjectIndex()->getValue();
1103 TStageObjectId objectId = getMotionObjectId(type, index);
1104 pf.m_fx->getAttributes()->setMotionPoints(getColumnMotionPoints(
1105 m_xsh, m_frame, pf.m_columnIndex, objectId, m_isPreview,
1106 shutterStart, shutterEnd, traceResolution));
1107 }
1108 }
1109
1110 } else {
1111 // The follow-ups traduce their PlacedFx::m_aff into an NaAffineFx,
1112 // instead
1113 inputFx = getFxWithColumnMovements(inputPF);
1114 inputFx = TFxUtil::makeAffine(inputFx, pf.m_aff.inv());
1115 }
1116
1117 if (!pf.m_fx->connect(pf.m_fx->getInputPortName(i), inputFx.getPointer()))
1118 assert(!"Could not connect ports!");
1119 }
1120 }
1121
1122 // The xsheet-like input port is activated and brought upwards whenever it is
1123 // both
1124 // specified by the fx, and there is no input fx attached to it.
1125 if (pf.m_fx->getXsheetPort() && pf.m_fx->getXsheetPort()->getFx() == 0)
1126 pf.m_leftXsheetPort = pf.m_fx->getXsheetPort();
1127
1128 return pf;
1129 }
1130
1131 //***************************************************************************************************
1132 // Exported Render-Tree building functions
1133 //***************************************************************************************************
1134
buildSceneFx(ToonzScene * scene,TXsheet * xsh,double row,int whichLevels,int shrink,bool isPreview)1135 TFxP buildSceneFx(ToonzScene *scene, TXsheet *xsh, double row, int whichLevels,
1136 int shrink, bool isPreview) {
1137 FxBuilder builder(scene, xsh, row, whichLevels, isPreview);
1138 TFxP fx = builder.buildFx();
1139 TStageObjectId cameraId;
1140 if (isPreview)
1141 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
1142 else
1143 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
1144 TStageObject *cameraPegbar = xsh->getStageObject(cameraId);
1145 assert(cameraPegbar);
1146 TCamera *camera = cameraPegbar->getCamera();
1147 assert(camera);
1148
1149 TAffine aff = getDpiAffine(camera).inv();
1150 if (shrink > 1) {
1151 double fac = 0.5 * (1.0 / shrink - 1.0);
1152 aff = TTranslation(fac * camera->getRes().lx, fac * camera->getRes().ly) *
1153 TScale(1.0 / shrink) * aff;
1154 }
1155
1156 fx = TFxUtil::makeAffine(fx, aff);
1157 if (fx) fx->setName(L"CameraDPI and Shrink NAffineFx");
1158
1159 fx = TFxUtil::makeOver(
1160 TFxUtil::makeColorCard(scene->getProperties()->getBgColor()), fx);
1161 return fx;
1162 }
1163
1164 //===================================================================
1165
buildSceneFx(ToonzScene * scene,TXsheet * xsh,double row,int shrink,bool isPreview)1166 TFxP buildSceneFx(ToonzScene *scene, TXsheet *xsh, double row, int shrink,
1167 bool isPreview) {
1168 int whichLevels =
1169 scene->getProperties()->getOutputProperties()->getWhichLevels();
1170 return buildSceneFx(scene, xsh, row, whichLevels, shrink, isPreview);
1171 }
1172
1173 //===================================================================
1174
buildSceneFx(ToonzScene * scene,double row,int shrink,bool isPreview)1175 TFxP buildSceneFx(ToonzScene *scene, double row, int shrink, bool isPreview) {
1176 return buildSceneFx(scene, scene->getXsheet(), row, shrink, isPreview);
1177 }
1178
1179 //===================================================================
1180
buildSceneFx(ToonzScene * scene,TXsheet * xsh,double row,const TFxP & root,bool isPreview)1181 TFxP buildSceneFx(ToonzScene *scene, TXsheet *xsh, double row, const TFxP &root,
1182 bool isPreview) {
1183 int whichLevels =
1184 scene->getProperties()->getOutputProperties()->getWhichLevels();
1185 FxBuilder builder(scene, xsh, row, whichLevels, isPreview);
1186 return builder.buildFx(root, BSFX_NO_TR);
1187 }
1188
1189 //===================================================================
1190
buildSceneFx(ToonzScene * scene,double row,const TFxP & root,bool isPreview)1191 TFxP buildSceneFx(ToonzScene *scene, double row, const TFxP &root,
1192 bool isPreview) {
1193 return buildSceneFx(scene, scene->getXsheet(), row, root, isPreview);
1194 }
1195
1196 //===================================================================
1197
1198 //! Similar to buildSceneFx(ToonzScene *scene, double row, const TFxP &root,
1199 //! bool isPreview) method, build the sceneFx
1200 //! adding also camera transformations. Used for Preview Fx function.
buildPartialSceneFx(ToonzScene * scene,double row,const TFxP & root,int shrink,bool isPreview)1201 DVAPI TFxP buildPartialSceneFx(ToonzScene *scene, double row, const TFxP &root,
1202 int shrink, bool isPreview) {
1203 int whichLevels =
1204 scene->getProperties()->getOutputProperties()->getWhichLevels();
1205 FxBuilder builder(scene, scene->getXsheet(), row, whichLevels, isPreview);
1206 TFxP fx = builder.buildFx(
1207 root, BSFX_Transforms_Enum(BSFX_CAMERA_TR | BSFX_COLUMN_TR));
1208
1209 TXsheet *xsh = scene->getXsheet();
1210 TStageObjectId cameraId;
1211 if (isPreview)
1212 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
1213 else
1214 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
1215 TStageObject *cameraPegbar = xsh->getStageObject(cameraId);
1216 assert(cameraPegbar);
1217 TCamera *camera = cameraPegbar->getCamera();
1218 assert(camera);
1219
1220 TAffine aff = getDpiAffine(camera).inv();
1221 if (shrink > 1) {
1222 double fac = 0.5 * (1.0 / shrink - 1.0);
1223
1224 aff = TTranslation(fac * camera->getRes().lx, fac * camera->getRes().ly) *
1225 TScale(1.0 / shrink) * aff;
1226 }
1227
1228 fx = TFxUtil::makeAffine(fx, aff);
1229 return fx;
1230 }
1231
1232 //===================================================================
1233
buildPartialSceneFx(ToonzScene * scene,TXsheet * xsheet,double row,const TFxP & root,int shrink,bool isPreview)1234 DVAPI TFxP buildPartialSceneFx(ToonzScene *scene, TXsheet *xsheet, double row,
1235 const TFxP &root, int shrink, bool isPreview) {
1236 int whichLevels =
1237 scene->getProperties()->getOutputProperties()->getWhichLevels();
1238 FxBuilder builder(scene, xsheet, row, whichLevels, isPreview);
1239 TFxP fx = builder.buildFx(
1240 root, BSFX_Transforms_Enum(BSFX_CAMERA_TR | BSFX_COLUMN_TR));
1241
1242 TStageObjectId cameraId;
1243 if (isPreview)
1244 cameraId = xsheet->getStageObjectTree()->getCurrentPreviewCameraId();
1245 else
1246 cameraId = xsheet->getStageObjectTree()->getCurrentCameraId();
1247 TStageObject *cameraPegbar = xsheet->getStageObject(cameraId);
1248 assert(cameraPegbar);
1249 TCamera *camera = cameraPegbar->getCamera();
1250 assert(camera);
1251
1252 TAffine aff = getDpiAffine(camera).inv();
1253 if (shrink > 1) {
1254 double fac = 0.5 * (1.0 / shrink - 1.0);
1255
1256 aff = TTranslation(fac * camera->getRes().lx, fac * camera->getRes().ly) *
1257 TScale(1.0 / shrink) * aff;
1258 }
1259
1260 fx = TFxUtil::makeAffine(fx, aff);
1261 return fx;
1262 }
1263
1264 //===================================================================
1265
1266 /*!
1267 Builds the post-rendering fxs tree - that is, all fxs between the xsheet node
1268 and
1269 current output node.
1270
1271 This function can be used to isolate global post-processing fxs that typically
1272 do not
1273 contribute to scene compositing. When encountered, the xsheet node is \a not
1274 xpanded - it must be replaced manually.
1275 */
buildPostSceneFx(ToonzScene * scene,double frame,int shrink,bool isPreview)1276 DVAPI TFxP buildPostSceneFx(ToonzScene *scene, double frame, int shrink,
1277 bool isPreview) {
1278 // NOTE: Should whichLevels access output AND PREVIEW settings?
1279 int whichLevels =
1280 scene->getProperties()->getOutputProperties()->getWhichLevels();
1281
1282 TXsheet *xsh = scene->getXsheet();
1283 if (!xsh) xsh = scene->getXsheet();
1284
1285 // Do not expand the xsheet node
1286 FxBuilder builder(scene, xsh, frame, whichLevels, isPreview, false);
1287
1288 TFxP fx = builder.buildFx();
1289
1290 TStageObjectId cameraId;
1291 if (isPreview)
1292 cameraId = xsh->getStageObjectTree()->getCurrentPreviewCameraId();
1293 else
1294 cameraId = xsh->getStageObjectTree()->getCurrentCameraId();
1295 TStageObject *cameraPegbar = xsh->getStageObject(cameraId);
1296 assert(cameraPegbar);
1297 TCamera *camera = cameraPegbar->getCamera();
1298 assert(camera);
1299
1300 TAffine aff = getDpiAffine(camera).inv();
1301
1302 if (shrink > 1) {
1303 double fac = 0.5 * (1.0 / shrink - 1.0);
1304 aff = TTranslation(fac * camera->getRes().lx, fac * camera->getRes().ly) *
1305 TScale(1.0 / shrink) * aff;
1306 }
1307
1308 if (!aff.isIdentity()) fx = TFxUtil::makeAffine(fx, aff);
1309
1310 return fx;
1311 }
1312
1313 //===================================================================
1314
buildSceneFx(ToonzScene * scene,double frame,TXsheet * xsh,const TFxP & root,BSFX_Transforms_Enum transforms,bool isPreview,int whichLevels,int shrink)1315 DVAPI TFxP buildSceneFx(ToonzScene *scene, double frame, TXsheet *xsh,
1316 const TFxP &root, BSFX_Transforms_Enum transforms,
1317 bool isPreview, int whichLevels, int shrink) {
1318 // NOTE: Should whichLevels access output AND PREVIEW settings?
1319 if (whichLevels == -1)
1320 whichLevels =
1321 scene->getProperties()->getOutputProperties()->getWhichLevels();
1322
1323 if (!xsh) xsh = scene->getXsheet();
1324
1325 FxBuilder builder(scene, xsh, frame, whichLevels, isPreview);
1326
1327 TFxP fx = root ? builder.buildFx(root, transforms) : builder.buildFx();
1328
1329 TStageObjectId cameraId =
1330 isPreview ? xsh->getStageObjectTree()->getCurrentPreviewCameraId()
1331 : xsh->getStageObjectTree()->getCurrentCameraId();
1332
1333 TStageObject *cameraPegbar = xsh->getStageObject(cameraId);
1334 assert(cameraPegbar);
1335
1336 TCamera *camera = cameraPegbar->getCamera();
1337 assert(camera);
1338
1339 TAffine aff;
1340 if (transforms & BSFX_CAMERA_DPI_TR) aff = getDpiAffine(camera).inv();
1341
1342 if (shrink > 1) {
1343 double fac = 0.5 * (1.0 / shrink - 1.0);
1344 aff = TTranslation(fac * camera->getRes().lx, fac * camera->getRes().ly) *
1345 TScale(1.0 / shrink) * aff;
1346 }
1347
1348 if (!aff.isIdentity()) fx = TFxUtil::makeAffine(fx, aff);
1349
1350 return fx;
1351 }
1352