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