1 //******************************************************************************
2 ///
3 /// @file frontend/simplefrontend.h
4 ///
5 /// @todo   What's in here?
6 ///
7 /// @copyright
8 /// @parblock
9 ///
10 /// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
11 /// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
12 ///
13 /// POV-Ray is free software: you can redistribute it and/or modify
14 /// it under the terms of the GNU Affero General Public License as
15 /// published by the Free Software Foundation, either version 3 of the
16 /// License, or (at your option) any later version.
17 ///
18 /// POV-Ray is distributed in the hope that it will be useful,
19 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
20 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 /// GNU Affero General Public License for more details.
22 ///
23 /// You should have received a copy of the GNU Affero General Public License
24 /// along with this program.  If not, see <http://www.gnu.org/licenses/>.
25 ///
26 /// ----------------------------------------------------------------------------
27 ///
28 /// POV-Ray is based on the popular DKB raytracer version 2.12.
29 /// DKBTrace was originally written by David K. Buck.
30 /// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
31 ///
32 /// @endparblock
33 ///
34 //******************************************************************************
35 
36 #ifndef POVRAY_FRONTEND_SIMPLEFRONTEND_H
37 #define POVRAY_FRONTEND_SIMPLEFRONTEND_H
38 
39 // Module config header file must be the first file included within POV-Ray unit header files
40 #include "frontend/configfrontend.h"
41 
42 #include <boost/scoped_ptr.hpp>
43 
44 #include "frontend/animationprocessing.h"
45 #include "frontend/renderfrontend.h"
46 #include "frontend/shelloutprocessing.h"
47 
48 namespace pov_base
49 {
50 class Image;
51 }
52 
53 namespace pov_frontend
54 {
55 
56 using namespace pov_base;
57 
58 class ImageProcessing;
59 class AnimationProcessing;
60 class ShelloutProcessing;
61 
62 enum State
63 {
64     kUnknown,
65     kReady,
66     kStarting,
67     kPreSceneShellout,
68     kPreFrameShellout,
69     kParsing,
70     kPausedParsing,
71     kRendering,
72     kPausedRendering,
73     kPostFrameShellout,
74     kPostSceneShellout,
75     kPostShelloutPause,
76     kStopping,
77     kStopped,
78     kFailed,
79     kDone
80 };
81 
82 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
83 class SimpleFrontend
84 {
85     public:
86         SimpleFrontend(POVMSContext ctx, POVMSAddress addr, POVMS_Object& msg,
87                        boost::function<Console *()> cfn,
88                        boost::function<Display *(unsigned int, unsigned int)> dfn,
89                        POVMS_Object *result = nullptr, shared_ptr<Console> console = shared_ptr<Console>());
90         ~SimpleFrontend();
91 
92         bool Start(POVMS_Object& opts, shared_ptr<Image> img = shared_ptr<Image>());
93         bool Stop();
94         bool Pause();
95         bool Resume();
96 
97         State Process();
98 
99         State GetState() const;
100 
101         shared_ptr<Console> GetConsole();
102         shared_ptr<Image> GetImage();
103         shared_ptr<Display> GetDisplay();
104     private:
105         RenderFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH> renderFrontend;
106         POVMSAddress backendAddress;
107         State state;
108         POVMS_Object options;
109         RenderFrontendBase::SceneId sceneId;
110         RenderFrontendBase::ViewId viewId;
111         shared_ptr<ImageProcessing> imageProcessing;
112         shared_ptr<AnimationProcessing> animationProcessing;
113         shared_ptr<ShelloutProcessing> shelloutProcessing;
114         boost::function<Console *()> createConsole;
115         boost::function<Display *(unsigned int, unsigned int)> createDisplay;
116 };
117 
118 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
SimpleFrontend(POVMSContext ctx,POVMSAddress addr,POVMS_Object & msg,boost::function<Console * ()> cfn,boost::function<Display * (unsigned int,unsigned int)> dfn,POVMS_Object * result,shared_ptr<Console> console)119 SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::SimpleFrontend(POVMSContext ctx, POVMSAddress addr, POVMS_Object& msg,
120                                                                         boost::function<Console *()> cfn,
121                                                                         boost::function<Display *(unsigned int, unsigned int)> dfn,
122                                                                         POVMS_Object *result, shared_ptr<Console> console) :
123     renderFrontend(ctx),
124     backendAddress(addr),
125     state(kReady),
126     createConsole(cfn),
127     createDisplay(dfn)
128 {
129     renderFrontend.ConnectToBackend(backendAddress, msg, result, console);
130 }
131 
132 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
~SimpleFrontend()133 SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::~SimpleFrontend()
134 {
135     renderFrontend.DisconnectFromBackend(backendAddress);
136     state = kUnknown;
137 }
138 
139 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
Start(POVMS_Object & opts,shared_ptr<Image> img)140 bool SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::Start(POVMS_Object& opts, shared_ptr<Image> img)
141 {
142     int width;
143     int height;
144 
145     if(state != kReady)
146         return false;
147 
148     animationProcessing.reset();
149 
150     POVMS_List declares;
151     if(opts.Exist(kPOVAttrib_Declare) == true)
152         opts.Get(kPOVAttrib_Declare, declares);
153 
154     POVMS_Object image_width(kPOVMSType_WildCard);
155     image_width.SetString(kPOVAttrib_Identifier, "image_width");
156     image_width.SetFloat(kPOVAttrib_Value, width = opts.TryGetInt(kPOVAttrib_Width, 160));
157     declares.Append(image_width);
158 
159     POVMS_Object image_height(kPOVMSType_WildCard);
160     image_height.SetString(kPOVAttrib_Identifier, "image_height");
161     image_height.SetFloat(kPOVAttrib_Value, height = opts.TryGetInt(kPOVAttrib_Height, 120));
162     declares.Append(image_height);
163 
164     opts.Set(kPOVAttrib_Declare, declares);
165 
166     if(opts.TryGetInt(kPOVAttrib_FinalFrame, 0) > 0)
167         animationProcessing = shared_ptr<AnimationProcessing>(new AnimationProcessing(opts));
168 
169     options = opts;
170 
171     if(opts.TryGetBool(kPOVAttrib_OutputToFile, true))
172     {
173         if (img != nullptr)
174             imageProcessing = shared_ptr<ImageProcessing>(new ImageProcessing(img));
175         else
176             imageProcessing = shared_ptr<ImageProcessing>(new ImageProcessing(options));
177     }
178 
179     Path ip (opts.TryGetString(kPOVAttrib_InputFile, ""));
180     shelloutProcessing.reset(new ShelloutProcessing(opts, UCS2toASCIIString(ip.GetFile()), width, height));
181 
182     state = kStarting;
183 
184     return true;
185 }
186 
187 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
Stop()188 bool SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::Stop()
189 {
190     switch(state)
191     {
192         case kStarting:
193             state = kStopped;
194             return true;
195 
196         case kPreSceneShellout:
197         case kPostFrameShellout:
198         case kPostSceneShellout:
199         case kPreFrameShellout:
200             // TODO: add support for stopping shellouts, then halting render
201             throw POV_EXCEPTION(kCannotHandleRequestErr, "Shellout code not active yet (how did we get here?)");
202 
203         case kParsing:
204             renderFrontend.StopParser(sceneId);
205             state = kStopping;
206             return true;
207         case kRendering:
208             renderFrontend.StopRender(viewId);
209             state = kStopping;
210             return true;
211     }
212 
213     return false;
214 }
215 
216 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
Pause()217 bool SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::Pause()
218 {
219     switch(state)
220     {
221         case kPreSceneShellout:
222         case kPreFrameShellout:
223         case kPostFrameShellout:
224             // TODO: if we are running a shellout, we should allow request of a pause, which will
225             // take effect after the shellout returns (i.e. it won't pause the program being run
226             // but will pause POV-Ray afterwards).
227             throw POV_EXCEPTION(kCannotHandleRequestErr, "Shellout code not active yet (how did we get here?)");
228             break;
229 
230         case kParsing:
231             renderFrontend.PauseParser(sceneId);
232             state = kPausedParsing;
233             return true;
234 
235         case kRendering:
236             renderFrontend.PauseRender(viewId);
237             state = kPausedRendering;
238             return true;
239     }
240 
241     return false;
242 }
243 
244 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
Resume()245 bool SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::Resume()
246 {
247     switch(state)
248     {
249         case kPausedParsing:
250             renderFrontend.ResumeParser(sceneId);
251             state = kParsing;
252             return true;
253         case kPausedRendering:
254             renderFrontend.ResumeRender(viewId);
255             state = kRendering;
256             return true;
257 
258         case kPreSceneShellout:
259         case kPreFrameShellout:
260         case kPostFrameShellout:
261             // TODO: clear any pause that was requested while a shellout was running
262             // (would not have been acted on yet)
263             throw POV_EXCEPTION(kCannotHandleRequestErr, "Shellout code not active yet (how did we get here?)");
264     }
265 
266     return false;
267 }
268 
269 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
Process()270 State SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::Process()
271 {
272     switch(state)
273     {
274         case kReady:
275             return kReady;
276         case kStarting:
277             try
278             {
279                 if (animationProcessing != nullptr)
280                 {
281                     options = animationProcessing->GetFrameRenderOptions();
282                     if (imageProcessing != nullptr)
283                         options.SetUCS2String(kPOVAttrib_OutputFile, imageProcessing->GetOutputFilename(options, animationProcessing->GetNominalFrameNumber(), animationProcessing->GetFrameNumberDigits()).c_str());
284                 }
285                 else
286                     if (imageProcessing != nullptr)
287                         options.SetUCS2String(kPOVAttrib_OutputFile, imageProcessing->GetOutputFilename(options, 0, 0).c_str());
288             }
289             catch(pov_base::Exception&)
290             {
291                 state = kFailed;
292                 // TODO - output failure message
293                 return kFailed;
294             }
295 
296             try { sceneId = renderFrontend.CreateScene(backendAddress, options, createConsole); }
297             catch(pov_base::Exception&)
298             {
299                 state = kFailed;
300                 // TODO - output failure message
301                 return kFailed;
302             }
303 
304             try { renderFrontend.StartParser(sceneId, options); }
305             catch(pov_base::Exception&)
306             {
307                 state = kFailed;
308                 // TODO - output failure message
309                 return kFailed;
310             }
311 
312             state = kParsing;
313 
314             return kParsing;
315 
316         case kPreSceneShellout:
317         case kPostFrameShellout:
318         case kPostSceneShellout:
319         case kPreFrameShellout:
320             // TODO: add support for shellouts
321             throw POV_EXCEPTION(kCannotHandleRequestErr, "Shellout code not active yet (how did we get here?)");
322 
323         case kParsing:
324             switch(renderFrontend.GetSceneState(sceneId))
325             {
326                 case SceneData::Scene_Failed:
327                     state = kStopped;
328                     return kStopped;
329                 case SceneData::Scene_Stopping:
330                     state = kStopping;
331                     return kStopping;
332                 case SceneData::Scene_Ready:
333                     try { viewId = renderFrontend.CreateView(sceneId, options, imageProcessing, createDisplay); }
334                     catch(pov_base::Exception&)
335                     {
336                         state = kFailed;
337                         // TODO - output failure message
338                         return kFailed;
339                     }
340 
341                     try { renderFrontend.StartRender(viewId, options); }
342                     catch(pov_base::Exception& e)
343                     {
344                         if(e.codevalid() && (e.code() == kImageAlreadyRenderedErr)) // TODO FIXME - This can be done much simpler: Just do nothing!
345                         {
346                             // this is not a failure; continue has been requested and
347                             // the file has already been rendered, so we skip it.
348                             if ((animationProcessing != nullptr) && (animationProcessing->MoreFrames() == true))
349                             {
350                                 animationProcessing->ComputeNextFrame();
351                                 state = kStarting;
352                                 return kStarting;
353                             }
354                             else
355                             {
356                                 state = kDone;
357                                 return kDone;
358                             }
359                         }
360                         state = kFailed;
361                         // TODO - output failure message
362                         return kFailed;
363                     }
364 
365                     state = kRendering;
366 
367                     return kRendering;
368 
369                 default:
370                     return state;
371             }
372             POV_FRONTEND_ASSERT(false); // All cases of the preceding switch should return.
373 
374         case kRendering:
375             switch(renderFrontend.GetViewState(viewId))
376             {
377                 case ViewData::View_Failed:
378                     state = kStopped;
379                     return kStopped;
380                 case ViewData::View_Stopping:
381                     state = kStopping;
382                     return kStopping;
383                 case ViewData::View_Rendered:
384                     if (imageProcessing != nullptr)
385                     {
386                         try
387                         {
388                             if (animationProcessing != nullptr)
389                                 imageProcessing->WriteImage(options, animationProcessing->GetNominalFrameNumber(), animationProcessing->GetFrameNumberDigits());
390                             else
391                                 imageProcessing->WriteImage(options);
392                         }
393                         catch(...)
394                         {
395                             state = kFailed;
396                             // TODO - output failure message
397                             return kFailed;
398                         }
399                     }
400 
401                     if ((animationProcessing != nullptr) && (animationProcessing->MoreFrames() == true))
402                     {
403                         try { renderFrontend.CloseView(viewId); } catch(...) { } // Ignore any error here!
404                         try { renderFrontend.CloseScene(sceneId); } catch(...) { } // Ignore any error here!
405                         animationProcessing->ComputeNextFrame();
406                         state = kStarting;
407                         return kStarting;
408                     }
409                     else
410                     {
411                         state = kDone;
412                         return kDone;
413                     }
414 
415                 default:
416                     return state;
417             }
418             POV_FRONTEND_ASSERT(false); // All cases of the preceding switch should return.
419 
420         case kStopping:
421             if(renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Ready || renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Failed)
422             {
423                 state = kStopped;
424                 return kStopped;
425             }
426             else if(renderFrontend.GetViewState(viewId) == ViewData::View_Rendered)
427             {
428                 state = kStopped;
429                 return kStopped;
430             }
431             return kRendering;
432         case kStopped:
433         case kFailed:
434         case kDone:
435             try { renderFrontend.CloseView(viewId); } catch(...) { } // Ignore any error here!
436             try { renderFrontend.CloseScene(sceneId); } catch(...) { } // Ignore any error here!
437             animationProcessing.reset();
438 
439             state = kReady;
440 
441             return kReady;
442 
443         default:
444             return state;
445     }
446     POV_FRONTEND_ASSERT(false); // All cases of the preceding switch should return.
447 }
448 
449 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
GetState()450 State SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::GetState() const
451 {
452     return state;
453 }
454 
455 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
GetConsole()456 shared_ptr<Console> SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::GetConsole()
457 {
458     return renderFrontend.GetConsole(sceneId);
459 }
460 
461 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
GetImage()462 shared_ptr<Image> SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::GetImage()
463 {
464     return renderFrontend.GetImage(viewId);
465 }
466 
467 template<class PARSER_MH, class FILE_MH, class RENDER_MH, class IMAGE_MH>
GetDisplay()468 shared_ptr<Display> SimpleFrontend<PARSER_MH, FILE_MH, RENDER_MH, IMAGE_MH>::GetDisplay()
469 {
470     return renderFrontend.GetDisplay(viewId);
471 }
472 
473 }
474 
475 #endif // POVRAY_FRONTEND_SIMPLEFRONTEND_H
476