1403 lines
44 KiB
C++
1403 lines
44 KiB
C++
/*******************************************************************************
|
|
* vfe.cpp
|
|
*
|
|
* This module contains the C++ implementation for the virtual frontend.
|
|
*
|
|
* Author: Christopher J. Cason
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/vfe/vfe.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable : 4244)
|
|
#pragma warning(disable : 4267)
|
|
#endif
|
|
|
|
#include "frame.h"
|
|
#include "povray.h"
|
|
#include "vfe.h"
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace vfe
|
|
{
|
|
|
|
using namespace pov_base;
|
|
using boost::format;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class POVMSMessageDetails
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class POVMSMessageDetails
|
|
{
|
|
public:
|
|
POVMSMessageDetails (POVMS_Object &Obj);
|
|
virtual ~POVMSMessageDetails () {} ;
|
|
string GetContext (int NumLines) ;
|
|
|
|
protected:
|
|
string File ;
|
|
UCS2String UCS2File;
|
|
string URL ;
|
|
string Message ;
|
|
POVMSInt Line ;
|
|
POVMSInt Col ;
|
|
POVMSLong Offset ;
|
|
} ;
|
|
|
|
POVMSMessageDetails::POVMSMessageDetails (POVMS_Object& Obj)
|
|
{
|
|
char buffer [2048] = "";
|
|
UCS2 ubuffer [2048];
|
|
POVMSInt l = sizeof (ubuffer);
|
|
POVMSLong ll ;
|
|
POVMSObject msgobj (Obj());
|
|
POVMSObjectPtr msg = &msgobj;
|
|
|
|
Line = Col = 0 ;
|
|
Offset = -1 ;
|
|
ubuffer[0] = 0 ;
|
|
|
|
if (POVMSUtil_GetUCS2String (msg, kPOVAttrib_FileName, ubuffer, &l) == kNoErr)
|
|
{
|
|
UCS2File = ubuffer ;
|
|
File = UCS2toASCIIString (UCS2File);
|
|
}
|
|
if (POVMSUtil_GetLong (msg, kPOVAttrib_Line, &ll) == kNoErr)
|
|
Line = POVMSInt(ll) ;
|
|
if (POVMSUtil_GetLong (msg, kPOVAttrib_Column, &ll) == kNoErr)
|
|
Col = POVMSInt(ll + 1) ;
|
|
if(POVMSUtil_GetLong(msg, kPOVAttrib_FilePosition, &ll) == kNoErr)
|
|
Offset = ll ;
|
|
l = sizeof (buffer) ;
|
|
if (POVMSUtil_GetString (msg, kPOVAttrib_EnglishText, buffer, &l) == kNoErr)
|
|
Message = buffer ;
|
|
|
|
POVMSObject_Delete(msg);
|
|
}
|
|
|
|
string POVMSMessageDetails::GetContext (int NumLines)
|
|
{
|
|
return ("") ;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ParseErrorDetails, ParseWarningDetails
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class ParseWarningDetails : public POVMSMessageDetails
|
|
{
|
|
public:
|
|
ParseWarningDetails (POVMS_Object &Obj) : POVMSMessageDetails (Obj) {} ;
|
|
virtual ~ParseWarningDetails () {} ;
|
|
|
|
public:
|
|
using POVMSMessageDetails::File ;
|
|
using POVMSMessageDetails::UCS2File ;
|
|
using POVMSMessageDetails::Message ;
|
|
using POVMSMessageDetails::Line ;
|
|
using POVMSMessageDetails::Col ;
|
|
using POVMSMessageDetails::Offset ;
|
|
} ;
|
|
|
|
class ParseErrorDetails : public POVMSMessageDetails
|
|
{
|
|
public:
|
|
ParseErrorDetails (POVMS_Object &Obj) : POVMSMessageDetails (Obj) {} ;
|
|
virtual ~ParseErrorDetails () {} ;
|
|
|
|
public:
|
|
using POVMSMessageDetails::File ;
|
|
using POVMSMessageDetails::UCS2File ;
|
|
using POVMSMessageDetails::Message ;
|
|
using POVMSMessageDetails::Line ;
|
|
using POVMSMessageDetails::Col ;
|
|
using POVMSMessageDetails::Offset ;
|
|
} ;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class vfeConsole
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
vfeConsole::vfeConsole(vfeSession *session, int width) : Console(width == -1 ? session->GetConsoleWidth() : width)
|
|
{
|
|
m_Session = session;
|
|
Initialise();
|
|
}
|
|
|
|
vfeConsole::~vfeConsole()
|
|
{
|
|
}
|
|
|
|
void vfeConsole::Initialise()
|
|
{
|
|
rawBuffer [0] = '\0' ;
|
|
}
|
|
|
|
void vfeConsole::BufferOutput(const char *str, unsigned int chars, vfeSession::MessageType mType)
|
|
{
|
|
char *s ;
|
|
|
|
// HACK FIXME - this is to prevent duplicate messages
|
|
if (m_Session->HadErrorMessage() && strncmp (str, "Fatal error in parser: ", 23) == 0)
|
|
return ;
|
|
|
|
if (str [0] == '\n' && str [1] == '\0')
|
|
{
|
|
m_Session->AppendStreamMessage (mType, rawBuffer) ;
|
|
rawBuffer [0] = '\0' ;
|
|
return ;
|
|
}
|
|
|
|
size_t sLen = chars ;
|
|
if (sLen == 0)
|
|
sLen = strlen (str) ;
|
|
size_t bLen = strlen (rawBuffer) ;
|
|
if (sLen > sizeof (rawBuffer) - bLen - 1)
|
|
sLen = sizeof (rawBuffer) - bLen - 1 ;
|
|
strncat (rawBuffer, str, sLen) ;
|
|
if ((s = strrchr (rawBuffer, '\n')) != NULL)
|
|
{
|
|
*s++ = '\0' ;
|
|
m_Session->AppendStreamMessage (mType, rawBuffer) ;
|
|
strcpy (rawBuffer, s) ;
|
|
}
|
|
}
|
|
|
|
void vfeConsole::Output(const char *str, vfeSession::MessageType mType)
|
|
{
|
|
BufferOutput (str, (unsigned int) strlen (str), mType) ;
|
|
BufferOutput ("\n", 1, mType) ;
|
|
}
|
|
|
|
void vfeConsole::Output(const string& str, vfeSession::MessageType mType)
|
|
{
|
|
Output (str.c_str(), mType) ;
|
|
}
|
|
|
|
void vfeConsole::Output(const string& str)
|
|
{
|
|
Output (str.c_str()) ;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class vfePlatformBase
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
vfePlatformBase::vfePlatformBase(vfeSession& session) : m_Session(&session), PlatformBase()
|
|
{
|
|
}
|
|
|
|
vfePlatformBase::~vfePlatformBase()
|
|
{
|
|
}
|
|
|
|
IStream *vfePlatformBase::CreateIStream(const unsigned int stype)
|
|
{
|
|
return (new IStream (stype)) ;
|
|
}
|
|
|
|
OStream *vfePlatformBase::CreateOStream(const unsigned int stype)
|
|
{
|
|
return (new OStream (stype)) ;
|
|
}
|
|
|
|
UCS2String vfePlatformBase::GetTemporaryPath(void)
|
|
{
|
|
return m_Session->GetTemporaryPath();
|
|
}
|
|
|
|
UCS2String vfePlatformBase::CreateTemporaryFile(void)
|
|
{
|
|
return m_Session->CreateTemporaryFile();
|
|
}
|
|
|
|
void vfePlatformBase::DeleteTemporaryFile(const UCS2String& filename)
|
|
{
|
|
m_Session->DeleteTemporaryFile(filename);
|
|
}
|
|
|
|
bool vfePlatformBase::ReadFileFromURL(OStream *file, const UCS2String& url, const UCS2String& referrer)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class vfeParserMessageHandler
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
vfeParserMessageHandler::vfeParserMessageHandler() : ParserMessageHandler()
|
|
{
|
|
m_Session = vfeSession::GetSessionFromThreadID();
|
|
}
|
|
|
|
vfeParserMessageHandler::~vfeParserMessageHandler()
|
|
{
|
|
}
|
|
|
|
void vfeParserMessageHandler::Options(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
if (Obj.TryGetBool (kPOVAttrib_OutputAlpha, false))
|
|
m_Session->SetUsingAlpha();
|
|
if (Obj.TryGetBool (kPOVAttrib_ClocklessAnimation, false))
|
|
m_Session->SetClocklessAnimation();
|
|
if (Obj.TryGetBool (kPOVAttrib_RealTimeRaytracing, false))
|
|
m_Session->SetRealTimeRaytracing();
|
|
ParserMessageHandler::Options (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeParserMessageHandler::Statistics(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
ParserMessageHandler::Statistics (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeParserMessageHandler::Progress(Console *Con, POVMS_Object& Obj, bool verbose)
|
|
{
|
|
switch(Obj.GetType(kPOVMSObjectClassID))
|
|
{
|
|
case kPOVObjectClass_ParserProgress:
|
|
{
|
|
m_Session->AppendStatusMessage (format ("Parsing %uK tokens") % (Obj.GetLong (kPOVAttrib_CurrentTokenCount) / 1000));
|
|
break;
|
|
}
|
|
case kPOVObjectClass_BoundingProgress:
|
|
{
|
|
m_Session->AppendStatusMessage (format ("Constructed %uK BSP nodes") % (Obj.GetLong (kPOVAttrib_CurrentNodeCount) / 1000));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void vfeParserMessageHandler::Warning(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
ParseWarningDetails d (Obj) ;
|
|
|
|
if (d.Message == "")
|
|
return ;
|
|
|
|
// as we provide special treatment for warning messages here if we're not
|
|
// optimized for console output, we don't duplicate them to the console
|
|
// regardless of whether or not conout is set.
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendWarningMessage (d.Message, d.UCS2File, d.Line, d.Col) ;
|
|
|
|
if (!d.File.empty() && (d.Line > 0))
|
|
{
|
|
format f = format ("File '%s' line %d: %s") % d.File % d.Line % d.Message ;
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (f) ;
|
|
else if (conout)
|
|
Con->puts (f.str().c_str()) ;
|
|
}
|
|
else
|
|
{
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (d.Message) ;
|
|
else if (conout)
|
|
Con->puts (d.Message.c_str()) ;
|
|
}
|
|
}
|
|
|
|
void vfeParserMessageHandler::Error(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
ParseErrorDetails d (Obj) ;
|
|
|
|
if (d.Message == "" && (d.File == "" || d.Line <= 0))
|
|
return ;
|
|
|
|
// as we provide special treatment for parser errors here if we're not
|
|
// optimized for console output, we don't duplicate them to the console
|
|
// regardless of whether or not conout is set.
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendErrorMessage (d.Message, d.UCS2File, d.Line, d.Col) ;
|
|
|
|
if (!d.Message.empty())
|
|
{
|
|
if (!d.File.empty() && (d.Line > 0))
|
|
{
|
|
format f = format ("File '%s' line %d: %s") % d.File % d.Line % d.Message ;
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (f) ;
|
|
else if (conout)
|
|
Con->puts (f.str().c_str()) ;
|
|
}
|
|
else
|
|
{
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (d.Message) ;
|
|
if (conout)
|
|
if (m_Session->m_OptimizeForConsoleOutput == true)
|
|
Con->puts (d.Message.c_str()) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
format f = format ("Parse error in file '%s' at line %d") % d.File % d.Line ;
|
|
if (m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (f) ;
|
|
if (conout)
|
|
if (m_Session->m_OptimizeForConsoleOutput == true)
|
|
Con->puts (f.str().c_str()) ;
|
|
}
|
|
}
|
|
|
|
void vfeParserMessageHandler::FatalError(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
m_Session->SetFailed();
|
|
Error (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeParserMessageHandler::DebugInfo(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
string str(Obj.GetString(kPOVAttrib_EnglishText));
|
|
if (m_Session->m_OptimizeForConsoleOutput == true)
|
|
{
|
|
if (conout)
|
|
Con->puts (str.c_str()) ;
|
|
}
|
|
else
|
|
m_Session->AppendStreamMessage (vfeSession::mDebug, str.c_str()) ;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class vfeRenderMessageHandler
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
vfeRenderMessageHandler::vfeRenderMessageHandler() : RenderMessageHandler()
|
|
{
|
|
m_Session = vfeSession::GetSessionFromThreadID();
|
|
}
|
|
|
|
vfeRenderMessageHandler::~vfeRenderMessageHandler()
|
|
{
|
|
}
|
|
|
|
void vfeRenderMessageHandler::Options(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
RenderMessageHandler::Options (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeRenderMessageHandler::Statistics(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
RenderMessageHandler::Statistics (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeRenderMessageHandler::Progress(Console *Con, POVMS_Object& Obj, bool verbose)
|
|
{
|
|
switch (Obj.GetType(kPOVMSObjectClassID))
|
|
{
|
|
case kPOVObjectClass_PhotonProgress:
|
|
{
|
|
int cpc (Obj.GetInt (kPOVAttrib_CurrentPhotonCount)) ;
|
|
m_Session->AppendStatusMessage (format ("Photon count %u") % cpc, 250) ;
|
|
break;
|
|
}
|
|
case kPOVObjectClass_RadiosityProgress:
|
|
{
|
|
int pc (Obj.GetInt (kPOVAttrib_Pixels)) ;
|
|
int cc (Obj.GetInt (kPOVAttrib_PixelsCompleted)) ;
|
|
m_Session->SetPixelsRendered(cc, pc);
|
|
int percent = pc > 0 ? int ((cc * 100.0) / pc) : 0 ;
|
|
m_Session->SetPercentComplete (percent);
|
|
m_Session->AppendStatusMessage (format ("Performing radiosity pretrace: %d of %d pixels (%d%%)") % cc % pc % percent, 250) ;
|
|
break;
|
|
}
|
|
case kPOVObjectClass_RenderProgress:
|
|
{
|
|
int pc (Obj.GetInt (kPOVAttrib_Pixels)) ;
|
|
int cc (Obj.GetInt (kPOVAttrib_PixelsCompleted)) ;
|
|
|
|
if (m_Session->GetRealTimeRaytracing() == false)
|
|
{
|
|
m_Session->SetPixelsRendered(cc, pc);
|
|
int percent = pc > 0 ? (int) ((cc * 100.0) / pc) : 0 ;
|
|
m_Session->SetPercentComplete (percent);
|
|
if (verbose == true || m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (format ("Rendered %u of %u pixels (%d%%)") % cc % pc % percent, 250) ;
|
|
}
|
|
else
|
|
{
|
|
m_Session->SetPixelsRendered(cc % pc, pc);
|
|
float elapsed = m_Session->GetElapsedTime() / 1000.0f;
|
|
float frames = (float) cc / pc;
|
|
float fps = frames / elapsed;
|
|
if (verbose == true || m_Session->m_OptimizeForConsoleOutput == false)
|
|
m_Session->AppendStatusMessage (format ("Rendered %g frames over %g seconds (%g FPS)") % frames % elapsed % fps, 250) ;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void vfeRenderMessageHandler::Warning(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
RenderMessageHandler::Warning (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeRenderMessageHandler::Error(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
m_Session->SetFailed();
|
|
RenderMessageHandler::Error (Con, Obj, conout) ;
|
|
}
|
|
|
|
void vfeRenderMessageHandler::FatalError(Console *Con, POVMS_Object& Obj, bool conout)
|
|
{
|
|
m_Session->SetFailed();
|
|
RenderMessageHandler::FatalError (Con, Obj, conout) ;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class vfeProcessRenderOptions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
vfeProcessRenderOptions::vfeProcessRenderOptions(vfeSession *Session) : ProcessRenderOptions(), m_Session(Session)
|
|
{
|
|
}
|
|
|
|
vfeProcessRenderOptions::~vfeProcessRenderOptions()
|
|
{
|
|
}
|
|
|
|
int vfeProcessRenderOptions::ReadSpecialOptionHandler(INI_Parser_Table *Table, char *Param, POVMSObjectPtr Obj)
|
|
{
|
|
return ProcessRenderOptions::ReadSpecialOptionHandler (Table, Param, Obj);
|
|
}
|
|
|
|
int vfeProcessRenderOptions::ReadSpecialSwitchHandler(Cmd_Parser_Table *Table, char *Param, POVMSObjectPtr Obj, bool On)
|
|
{
|
|
return ProcessRenderOptions::ReadSpecialSwitchHandler (Table, Param, Obj, On);
|
|
}
|
|
|
|
int vfeProcessRenderOptions::WriteSpecialOptionHandler(INI_Parser_Table *Table, POVMSObjectPtr Obj, OTextStream *S)
|
|
{
|
|
return ProcessRenderOptions::WriteSpecialOptionHandler (Table, Obj, S);
|
|
}
|
|
|
|
bool vfeProcessRenderOptions::WriteOptionFilter(INI_Parser_Table *Table)
|
|
{
|
|
return ProcessRenderOptions::WriteOptionFilter (Table);
|
|
}
|
|
|
|
int vfeProcessRenderOptions::ProcessUnknownString(char *String, POVMSObjectPtr Obj)
|
|
{
|
|
return ProcessRenderOptions::ProcessUnknownString (String, Obj);
|
|
}
|
|
|
|
ITextStream *vfeProcessRenderOptions::OpenFileForRead(const char *Name, POVMSObjectPtr Obj)
|
|
{
|
|
return (ProcessRenderOptions::OpenFileForRead (Name, Obj)) ;
|
|
}
|
|
|
|
OTextStream *vfeProcessRenderOptions::OpenFileForWrite(const char *Name, POVMSObjectPtr Obj)
|
|
{
|
|
return (ProcessRenderOptions::OpenFileForWrite (Name, Obj)) ;
|
|
}
|
|
|
|
void vfeProcessRenderOptions::ParseError(const char *format, ...)
|
|
{
|
|
char str[1024];
|
|
va_list marker;
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(str, sizeof(str)-2, format, marker);
|
|
va_end(marker);
|
|
|
|
m_Session->AppendStatusMessage (str);
|
|
m_Session->AppendErrorMessage (str) ;
|
|
m_Session->SetFailed();
|
|
}
|
|
|
|
void vfeProcessRenderOptions::ParseErrorAt(ITextStream *file, const char *format, ...)
|
|
{
|
|
char str[1024];
|
|
va_list marker;
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(str, sizeof(str)-2, format, marker);
|
|
va_end(marker);
|
|
|
|
m_Session->AppendStatusMessage (str);
|
|
m_Session->AppendErrorMessage (str, file->name(), file->line(), 0) ;
|
|
m_Session->SetFailed();
|
|
}
|
|
|
|
void vfeProcessRenderOptions::WriteError(const char *format, ...)
|
|
{
|
|
char str[1024];
|
|
va_list marker;
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(str, sizeof(str)-2, format, marker);
|
|
va_end(marker);
|
|
|
|
m_Session->AppendStatusMessage (str);
|
|
m_Session->AppendErrorMessage (str) ;
|
|
m_Session->SetFailed();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class VirtualFrontEnd
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
VirtualFrontEnd::VirtualFrontEnd(vfeSession& session, POVMSContext ctx, POVMSAddress addr, POVMS_Object& msg, POVMS_Object *result, shared_ptr<Console>& console) :
|
|
m_Session(&session), m_PlatformBase(session), renderFrontend (ctx)
|
|
{
|
|
backendAddress = addr ;
|
|
state = kReady ;
|
|
m_PostPauseState = kReady;
|
|
consoleResult = NULL ;
|
|
displayResult = NULL ;
|
|
m_PauseRequested = m_PausedAfterFrame = false;
|
|
renderFrontend.ConnectToBackend(backendAddress, msg, result, console);
|
|
}
|
|
|
|
VirtualFrontEnd::~VirtualFrontEnd()
|
|
{
|
|
// file-backed images may require a reference to PlatformBase to delete temporary files
|
|
// we need to explicitly delete it here since otherwise PlatformBase will have been destroyed
|
|
// before the shared_ptr does its cleanup
|
|
imageProcessing.reset();
|
|
if (backendAddress != POVMSInvalidAddress)
|
|
renderFrontend.DisconnectFromBackend(backendAddress);
|
|
state = kUnknown;
|
|
}
|
|
|
|
bool VirtualFrontEnd::Start(POVMS_Object& opts)
|
|
{
|
|
if (state != kReady)
|
|
return false;
|
|
|
|
m_Session->Clear();
|
|
animationProcessing.reset() ;
|
|
m_PauseRequested = m_PausedAfterFrame = false;
|
|
m_PostPauseState = kReady;
|
|
|
|
Path ip (m_Session->GetInputFilename());
|
|
shelloutProcessing.reset(m_Session->CreateShelloutProcessing(opts, UCS2toASCIIString(ip.GetFile()), m_Session->GetRenderWidth(), m_Session->GetRenderHeight())) ;
|
|
shelloutProcessing->SetCancelMessage("Render halted because the %1% shell-out ('%6%') requested POV-Ray to %5%.");
|
|
shelloutProcessing->SetSkipMessage("The %1% shell-out ('%3%') requested POV-Ray to %2%.");
|
|
|
|
POVMS_List declares;
|
|
if(opts.Exist(kPOVAttrib_Declare) == true)
|
|
opts.Get(kPOVAttrib_Declare, declares);
|
|
|
|
POVMS_Object image_width(kPOVMSType_WildCard);
|
|
image_width.SetString(kPOVAttrib_Identifier, "image_width");
|
|
image_width.SetFloat(kPOVAttrib_Value, opts.TryGetInt(kPOVAttrib_Width, 160));
|
|
declares.Append(image_width);
|
|
|
|
POVMS_Object image_height(kPOVMSType_WildCard);
|
|
image_height.SetString(kPOVAttrib_Identifier, "image_height");
|
|
image_height.SetFloat(kPOVAttrib_Value, opts.TryGetInt(kPOVAttrib_Height, 120));
|
|
declares.Append(image_height);
|
|
|
|
POVMS_Object input_file_name(kPOVMSType_WildCard);
|
|
input_file_name.SetString(kPOVAttrib_Identifier, "input_file_name");
|
|
input_file_name.SetString(kPOVAttrib_Value, UCS2toASCIIString(ip.GetFile()).c_str());
|
|
declares.Append(input_file_name);
|
|
|
|
int initialFrame = opts.TryGetInt (kPOVAttrib_InitialFrame, 0) ;
|
|
int finalFrame = opts.TryGetInt (kPOVAttrib_FinalFrame, 0) ;
|
|
if ((initialFrame == 0 && finalFrame == 0) || (initialFrame == 1 && finalFrame == 1))
|
|
{
|
|
POVMS_Object clock_delta(kPOVMSType_WildCard);
|
|
clock_delta.SetString(kPOVAttrib_Identifier, "clock_delta");
|
|
clock_delta.SetFloat(kPOVAttrib_Value, 0.0f);
|
|
declares.Append(clock_delta);
|
|
|
|
POVMS_Object final_clock(kPOVMSType_WildCard);
|
|
final_clock.SetString(kPOVAttrib_Identifier, "final_clock");
|
|
final_clock.SetFloat(kPOVAttrib_Value, 0.0f);
|
|
declares.Append(final_clock);
|
|
|
|
POVMS_Object final_frame(kPOVMSType_WildCard);
|
|
final_frame.SetString(kPOVAttrib_Identifier, "final_frame");
|
|
final_frame.SetFloat(kPOVAttrib_Value, 0.0f);
|
|
declares.Append(final_frame);
|
|
|
|
POVMS_Object frame_number(kPOVMSType_WildCard);
|
|
frame_number.SetString(kPOVAttrib_Identifier, "frame_number");
|
|
frame_number.SetFloat(kPOVAttrib_Value, 0.0f);
|
|
declares.Append(frame_number);
|
|
|
|
POVMS_Object initial_clock(kPOVMSType_WildCard);
|
|
initial_clock.SetString(kPOVAttrib_Identifier, "initial_clock");
|
|
initial_clock.SetFloat(kPOVAttrib_Value, 0.0f);
|
|
declares.Append(initial_clock);
|
|
|
|
POVMS_Object initial_frame(kPOVMSType_WildCard);
|
|
initial_frame.SetString(kPOVAttrib_Identifier, "initial_frame");
|
|
initial_frame.SetFloat(kPOVAttrib_Value, 0.0f);
|
|
declares.Append(initial_frame);
|
|
|
|
opts.Set(kPOVAttrib_Declare, declares);
|
|
// optimization: reset imageProcessing now even though the following assign
|
|
// will free the old pointer (if it exists). this can potentially free a
|
|
// significant amount of memory and may in some circumstances prevent the
|
|
// image allocation from failing. TODO: it may be useful to check whether
|
|
// we can re-use an old imageProcessing instance (e.g. if image options are
|
|
// the same).
|
|
imageProcessing.reset();
|
|
|
|
// TODO: update ImageProcessing with the means of accepting and caching
|
|
// blocks of pixels as opposed to individual ones, with a back-end that
|
|
// can serialize completed rows to the final image output file.
|
|
options = opts;
|
|
|
|
if (m_Session->OutputToFileSet())
|
|
{
|
|
imageProcessing = shared_ptr<ImageProcessing> (new ImageProcessing (opts));
|
|
UCS2String filename = imageProcessing->GetOutputFilename (opts, 0, 0);
|
|
options.SetUCS2String (kPOVAttrib_OutputFile, filename.c_str());
|
|
|
|
if ((imageProcessing->OutputIsStdout() || imageProcessing->OutputIsStderr()) && m_Session->ImageOutputToStdoutSupported() == false)
|
|
throw POV_EXCEPTION(kCannotOpenFileErr, "Image output to STDOUT/STDERR not supported on this platform");
|
|
|
|
// test access permission now to avoid surprise later after waiting for
|
|
// the render to complete.
|
|
if (imageProcessing->OutputIsStdout() == false && imageProcessing->OutputIsStderr() == false && m_Session->TestAccessAllowed(filename, true) == false)
|
|
{
|
|
string str ("IO Restrictions prohibit write access to '") ;
|
|
str += UCS2toASCIIString(filename);
|
|
str += "'";
|
|
throw POV_EXCEPTION(kCannotOpenFileErr, str);
|
|
}
|
|
shelloutProcessing->SetOutputFile(UCS2toASCIIString(filename));
|
|
m_Session->AdviseOutputFilename (filename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the output filename is set in Process()
|
|
m_Session->SetRenderingAnimation();
|
|
opts.Set(kPOVAttrib_Declare, declares);
|
|
imageProcessing.reset();
|
|
if (m_Session->OutputToFileSet())
|
|
imageProcessing = shared_ptr<ImageProcessing> (new ImageProcessing (opts)) ;
|
|
animationProcessing = shared_ptr<AnimationProcessing> (new AnimationProcessing (opts)) ;
|
|
options = animationProcessing->GetFrameRenderOptions () ;
|
|
}
|
|
|
|
state = kStarting;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VirtualFrontEnd::Stop()
|
|
{
|
|
bool result = false;
|
|
|
|
try
|
|
{
|
|
switch(state)
|
|
{
|
|
case kStarting:
|
|
state = kStopped;
|
|
m_Session->SetFailed();
|
|
result = true;
|
|
break;
|
|
|
|
case kPreSceneShellout:
|
|
case kPreFrameShellout:
|
|
case kPostFrameShellout:
|
|
case kPostSceneShellout:
|
|
if (shelloutProcessing->ShelloutRunning())
|
|
if (!shelloutProcessing->KillShellouts(2, false) && !shelloutProcessing->KillShellouts(1, true))
|
|
m_Session->AppendErrorAndStatusMessage("Failed to terminate currently-running shellout process") ;
|
|
if (state == kPostSceneShellout)
|
|
{
|
|
state = kDone;
|
|
return true;
|
|
}
|
|
m_Session->SetFailed();
|
|
state = kStopped;
|
|
result = true;
|
|
break;
|
|
|
|
case kPostShelloutPause:
|
|
m_Session->SetFailed();
|
|
state = kStopping;
|
|
result = true;
|
|
break;
|
|
|
|
case kParsing:
|
|
case kPausedParsing:
|
|
// the parser could be already in a finished state, even if it accepted a pause earlier
|
|
try { renderFrontend.StopParser(sceneId); } catch (pov_base::Exception&) { }
|
|
m_Session->SetFailed();
|
|
state = kStopping;
|
|
result = true;
|
|
break;
|
|
|
|
case kRendering:
|
|
case kPausedRendering:
|
|
m_Session->SetFailed();
|
|
if (m_PausedAfterFrame == true)
|
|
{
|
|
m_PausedAfterFrame = false;
|
|
state = kStopped;
|
|
}
|
|
else
|
|
{
|
|
// the renderer could be already in a finished state, even if it accepted a pause earlier
|
|
try { renderFrontend.StopRender(viewId); } catch (pov_base::Exception&) { }
|
|
state = kStopping;
|
|
}
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
catch (pov_base::Exception& e)
|
|
{
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorAndStatusMessage (e.what()) ;
|
|
}
|
|
try
|
|
{
|
|
shelloutProcessing->ProcessEvent(ShelloutProcessing::userAbort);
|
|
}
|
|
catch (pov_base::Exception& e)
|
|
{
|
|
// if it's a kCannotOpenFileErr, it means permission to run the process was denied
|
|
// we don't set failed in that case as we allow shelloutprocessing to handle it
|
|
if (!e.codevalid() || (e.code() != kCannotOpenFileErr))
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorAndStatusMessage (e.what()) ;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool VirtualFrontEnd::Pause()
|
|
{
|
|
try
|
|
{
|
|
switch(state)
|
|
{
|
|
case kParsing:
|
|
renderFrontend.PauseParser(sceneId);
|
|
state = kPausedParsing;
|
|
return true;
|
|
|
|
case kPreSceneShellout:
|
|
case kPreFrameShellout:
|
|
case kPostFrameShellout:
|
|
m_PauseRequested = true;
|
|
return true;
|
|
|
|
case kRendering:
|
|
renderFrontend.PauseRender(viewId);
|
|
state = kPausedRendering;
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
catch (pov_base::Exception&)
|
|
{
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VirtualFrontEnd::Resume()
|
|
{
|
|
try
|
|
{
|
|
switch(state)
|
|
{
|
|
case kPostShelloutPause:
|
|
state = m_PostPauseState;
|
|
return true;
|
|
|
|
case kPausedParsing:
|
|
if (renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Paused)
|
|
renderFrontend.ResumeParser(sceneId);
|
|
state = kParsing;
|
|
return true;
|
|
|
|
case kPausedRendering:
|
|
if (m_PausedAfterFrame)
|
|
{
|
|
state = kStarting;
|
|
m_PausedAfterFrame = false;
|
|
return true;
|
|
}
|
|
if (renderFrontend.GetViewState(viewId) == ViewData::View_Paused)
|
|
renderFrontend.ResumeRender(viewId);
|
|
state = kRendering;
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
catch (pov_base::Exception&)
|
|
{
|
|
return (false) ;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VirtualFrontEnd::HandleShelloutCancel()
|
|
{
|
|
if (!shelloutProcessing->RenderCancelled())
|
|
return false;
|
|
|
|
int code(shelloutProcessing->ExitCode());
|
|
string str(shelloutProcessing->GetCancelMessage());
|
|
if (code)
|
|
{
|
|
state = kFailed;
|
|
m_Session->SetFailed();
|
|
}
|
|
else
|
|
{
|
|
state = kStopped;
|
|
m_Session->SetSucceeded(true);
|
|
}
|
|
m_Session->AppendErrorMessage(str) ;
|
|
m_Session->AppendStatusMessage(str) ;
|
|
return true;
|
|
}
|
|
|
|
State VirtualFrontEnd::Process()
|
|
{
|
|
if (state == kReady)
|
|
return kReady;
|
|
|
|
switch(state)
|
|
{
|
|
case kStarting:
|
|
try
|
|
{
|
|
m_Session->SetSucceeded (false);
|
|
if (animationProcessing != NULL)
|
|
{
|
|
shelloutProcessing->SetFrameClock(animationProcessing->GetFrameNumber(), animationProcessing->GetClockValue());
|
|
if (shelloutProcessing->SkipNextFrame() == false)
|
|
{
|
|
UCS2String filename;
|
|
int frame = animationProcessing->GetFrameNumber() - animationProcessing->GetStartFrame() ;
|
|
options = animationProcessing->GetFrameRenderOptions ();
|
|
if (m_Session->OutputToFileSet())
|
|
{
|
|
filename = imageProcessing->GetOutputFilename (options, animationProcessing->GetFrameNumber(), animationProcessing->GetFrameNumberDigits());
|
|
options.SetUCS2String (kPOVAttrib_OutputFile, filename.c_str());
|
|
|
|
// test access permission now to avoid surprise later after waiting for
|
|
// the render to complete.
|
|
if (m_Session->TestAccessAllowed(filename, true) == false)
|
|
{
|
|
string str ("IO Restrictions prohibit write access to '") ;
|
|
str += UCS2toASCIIString(filename);
|
|
str += "'";
|
|
throw POV_EXCEPTION(kCannotOpenFileErr, str);
|
|
}
|
|
shelloutProcessing->SetOutputFile(UCS2toASCIIString(filename));
|
|
m_Session->AdviseOutputFilename (filename);
|
|
}
|
|
m_Session->AppendAnimationStatus (frame + 1, animationProcessing->GetTotalFrames(), filename) ;
|
|
}
|
|
}
|
|
|
|
bool hadPreScene = shelloutProcessing->HadPreScene();
|
|
|
|
// will do pre-scene instead if it hasn't yet been done
|
|
try
|
|
{
|
|
shelloutProcessing->ProcessEvent(ShelloutProcessing::preFrame);
|
|
}
|
|
catch (pov_base::Exception& e)
|
|
{
|
|
// if it's a kCannotOpenFileErr, it means permission to run the process was denied
|
|
// we don't set failed in that case as we allow shelloutprocessing to handle it
|
|
m_Session->AppendErrorAndStatusMessage (e.what()) ;
|
|
if (!e.codevalid() || (e.code() != kCannotOpenFileErr))
|
|
{
|
|
m_Session->SetFailed();
|
|
return state = kFailed;
|
|
}
|
|
}
|
|
|
|
// returns true if cancel has been requested
|
|
if (HandleShelloutCancel())
|
|
return state;
|
|
|
|
state = hadPreScene ? kPreFrameShellout : kPreSceneShellout;
|
|
return state;
|
|
}
|
|
catch(pov_base::Exception& e)
|
|
{
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorAndStatusMessage (e.what()) ;
|
|
return state = kFailed;
|
|
}
|
|
|
|
case kPreSceneShellout:
|
|
if (shelloutProcessing->ShelloutRunning() || HandleShelloutCancel())
|
|
return state;
|
|
|
|
// if a pause was requested by the user whilst the shellout was still running, do it now
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kStarting;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
|
|
// go back to kStarting; it won't run pre-scene again
|
|
return state = kStarting;
|
|
|
|
case kPreFrameShellout:
|
|
if (shelloutProcessing->ShelloutRunning() || HandleShelloutCancel())
|
|
return state;
|
|
|
|
if (shelloutProcessing->SkipNextFrame())
|
|
{
|
|
string str(shelloutProcessing->GetSkipMessage());
|
|
m_Session->AppendStatusMessage (str) ;
|
|
m_Session->AppendStreamMessage (vfeSession::mInformation, str.c_str()) ;
|
|
if ((animationProcessing != NULL) && (animationProcessing->MoreFrames() == true))
|
|
{
|
|
animationProcessing->ComputeNextFrame();
|
|
m_Session->SetPixelsRendered(0, m_Session->GetTotalPixels());
|
|
m_Session->SetPercentComplete(0);
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kStarting;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
return state = kStarting;
|
|
}
|
|
else
|
|
{
|
|
m_Session->SetSucceeded (true);
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kStopped;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
return state = kStopped;
|
|
}
|
|
}
|
|
|
|
// now set up the scene in preparation for parsing, then start the parser
|
|
try { sceneId = renderFrontend.CreateScene(backendAddress, options, boost::bind(&vfe::VirtualFrontEnd::CreateConsole, this)); }
|
|
catch(pov_base::Exception& e)
|
|
{
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorMessage (e.what()) ;
|
|
m_Session->AppendStatusMessage (e.what()) ;
|
|
return state = kFailed;
|
|
}
|
|
try { renderFrontend.StartParser(sceneId, options); }
|
|
catch(pov_base::Exception& e)
|
|
{
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorMessage (e.what()) ;
|
|
m_Session->AppendStatusMessage (e.what()) ;
|
|
return state = kFailed;
|
|
}
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kParsing;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
return state = kParsing;
|
|
|
|
case kParsing:
|
|
case kPausedParsing:
|
|
switch(renderFrontend.GetSceneState(sceneId))
|
|
{
|
|
case SceneData::Scene_Paused:
|
|
return state = kPausedParsing;
|
|
|
|
case SceneData::Scene_Failed:
|
|
m_Session->SetFailed();
|
|
return state = kStopped;
|
|
|
|
case SceneData::Scene_Stopping:
|
|
return state = kStopping;
|
|
|
|
case SceneData::Scene_Ready:
|
|
if (state == kPausedParsing)
|
|
{
|
|
// it's possible for the parser to transition to Scene_Ready after a successful pause request.
|
|
// this typically happens if the request comes in very close to the end of a parse, since the
|
|
// task thread only checks the pause state intermittently. we don't start the renderer in this
|
|
// case.
|
|
return state;
|
|
}
|
|
try { viewId = renderFrontend.CreateView(sceneId, options, imageProcessing, boost::bind(&vfe::VirtualFrontEnd::CreateDisplay, this, _1, _2, _3)); }
|
|
catch(pov_base::Exception& e)
|
|
{
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorMessage (e.what()) ;
|
|
m_Session->AppendStatusMessage (e.what()) ;
|
|
return state = kFailed;
|
|
}
|
|
try { renderFrontend.StartRender(viewId, options); }
|
|
catch(pov_base::Exception& e)
|
|
{
|
|
m_Session->ClearStatusMessages();
|
|
if (e.codevalid() && e.code() == kImageAlreadyRenderedErr)
|
|
{
|
|
// this is not a failure; continue has been requested and
|
|
// the file has already been rendered, so we skip it.
|
|
m_Session->AppendStatusMessage ("File already rendered and continue requested; skipping.") ;
|
|
m_Session->AppendStreamMessage (vfeSession::mInformation, "File already rendered and continue requested; skipping.") ;
|
|
|
|
/* [JG] the block here is a duplicate of actions done after
|
|
* the post frame shellout (that won't be reached because
|
|
* the image was already there).
|
|
*/
|
|
try { renderFrontend.CloseView(viewId); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
try { renderFrontend.CloseScene(sceneId); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
|
|
if ((animationProcessing != NULL) && (animationProcessing->MoreFrames() == true))
|
|
{
|
|
animationProcessing->ComputeNextFrame();
|
|
m_Session->SetPixelsRendered(0, m_Session->GetTotalPixels());
|
|
m_Session->SetPercentComplete(0);
|
|
return state = kStarting;
|
|
}
|
|
else
|
|
{
|
|
m_Session->SetSucceeded (true);
|
|
return state = kStopped;
|
|
}
|
|
}
|
|
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorMessage (e.what()) ;
|
|
m_Session->AppendStatusMessage (e.what()) ;
|
|
return state = kFailed;
|
|
}
|
|
|
|
// now we display the render window, if enabled
|
|
shared_ptr<Display> display(GetDisplay());
|
|
if (display != NULL)
|
|
{
|
|
vfeDisplay *disp = dynamic_cast<vfeDisplay *>(display.get());
|
|
if (disp != NULL)
|
|
disp->Show () ;
|
|
}
|
|
return state = kRendering;
|
|
}
|
|
return kParsing;
|
|
|
|
case kRendering:
|
|
case kPausedRendering:
|
|
switch(renderFrontend.GetViewState(viewId))
|
|
{
|
|
case ViewData::View_Paused:
|
|
return state = kPausedRendering;
|
|
|
|
case ViewData::View_Failed:
|
|
m_Session->SetFailed();
|
|
return state = kStopped;
|
|
|
|
case ViewData::View_Stopping:
|
|
return state = kStopping;
|
|
|
|
case ViewData::View_Rendered:
|
|
if (state == kPausedRendering)
|
|
{
|
|
// it's possible for the renderer to transition to View_Rendered after a successful pause request.
|
|
return kPausedRendering;
|
|
}
|
|
try
|
|
{
|
|
if (animationProcessing != NULL)
|
|
{
|
|
if (m_Session->OutputToFileSet())
|
|
m_Session->AdviseOutputFilename (imageProcessing->WriteImage(options, animationProcessing->GetFrameNumber(), animationProcessing->GetFrameNumberDigits()));
|
|
m_Session->AdviseFrameCompleted();
|
|
}
|
|
else
|
|
if (m_Session->OutputToFileSet())
|
|
m_Session->AdviseOutputFilename (imageProcessing->WriteImage(options));
|
|
}
|
|
catch (pov_base::Exception& e)
|
|
{
|
|
m_Session->SetFailed();
|
|
m_Session->AppendErrorMessage (e.what()) ;
|
|
m_Session->AppendStatusMessage (e.what()) ;
|
|
// TODO: perhaps we should allow them to pause the queue/insert render
|
|
// here if need be.
|
|
return state = kFailed;
|
|
}
|
|
try
|
|
{
|
|
shelloutProcessing->ProcessEvent(ShelloutProcessing::postFrame);
|
|
}
|
|
catch (pov_base::Exception& e)
|
|
{
|
|
// if it's a kCannotOpenFileErr, it means permission to run the process was denied
|
|
// we don't set failed in that case as we allow shelloutprocessing to handle it
|
|
m_Session->AppendErrorAndStatusMessage (e.what());
|
|
if (!e.codevalid() || (e.code() != kCannotOpenFileErr))
|
|
{
|
|
m_Session->SetFailed();
|
|
return state = kFailed;
|
|
}
|
|
}
|
|
|
|
// check for cancel here: if the return value is true, state has already been changed
|
|
if (HandleShelloutCancel())
|
|
return state;
|
|
|
|
return state = kPostFrameShellout;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return kRendering;
|
|
|
|
case kPostFrameShellout:
|
|
if (shelloutProcessing->ShelloutRunning() || HandleShelloutCancel())
|
|
return state;
|
|
if ((animationProcessing == NULL) || animationProcessing->MoreFrames() == false)
|
|
{
|
|
m_Session->SetSucceeded (true);
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kStopped;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
return state = kStopped;
|
|
}
|
|
if (shelloutProcessing->SkipAllFrames())
|
|
{
|
|
string str(shelloutProcessing->GetSkipMessage());
|
|
m_Session->SetSucceeded (true);
|
|
m_Session->AppendStatusMessage (str) ;
|
|
m_Session->AppendStreamMessage (vfeSession::mInformation, str.c_str()) ;
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kStopped;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
return state = kStopped;
|
|
}
|
|
/* [JG] the actions hereafter should be also done
|
|
* when the image already existed: tidy up the data before next frame or stop
|
|
*/
|
|
try { renderFrontend.CloseView(viewId); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
try { renderFrontend.CloseScene(sceneId); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
animationProcessing->ComputeNextFrame();
|
|
if (m_Session->GetPauseWhenDone())
|
|
{
|
|
// wait for a manual continue
|
|
m_PausedAfterFrame = true;
|
|
m_PauseRequested = false;
|
|
return state = kPausedRendering;
|
|
}
|
|
if (m_PauseRequested)
|
|
{
|
|
m_PostPauseState = kStarting;
|
|
m_PauseRequested = false;
|
|
return state = kPostShelloutPause;
|
|
}
|
|
return state = kStarting;
|
|
|
|
case kPostSceneShellout:
|
|
if (shelloutProcessing->ShelloutRunning())
|
|
return state;
|
|
return state = kDone;
|
|
|
|
case kPostShelloutPause:
|
|
break;
|
|
|
|
case kStopping:
|
|
if (renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Ready || renderFrontend.GetSceneState(sceneId) == SceneData::Scene_Failed)
|
|
return state = kStopped;
|
|
if (renderFrontend.GetViewState(viewId) == ViewData::View_Rendered || renderFrontend.GetViewState(viewId) == ViewData::View_Failed)
|
|
return state = kStopped;
|
|
return kStopping;
|
|
|
|
case kFailed:
|
|
m_Session->SetFailed();
|
|
// ShelloutProcessing ignores the fatal error event if it requested a cancel
|
|
try { shelloutProcessing->ProcessEvent(ShelloutProcessing::fatalError); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here */ }
|
|
return state = kStopped;
|
|
|
|
case kStopped:
|
|
try { renderFrontend.CloseView(viewId); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
try { renderFrontend.CloseScene(sceneId); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
animationProcessing.reset();
|
|
imageProcessing.reset();
|
|
|
|
// we only run the post-scene or failed action if we have passed the pre-scene point
|
|
// i.e. if we stop before pre-scene, we don't run post-scene
|
|
if (shelloutProcessing->HadPreScene())
|
|
{
|
|
if (m_Session->Failed())
|
|
{
|
|
// it is possible for us to get to kStopped without going through kFailed
|
|
// so we call the failed shellout event here just in case: ShelloutProcessing
|
|
// can handle being called twice for the same event.
|
|
try { shelloutProcessing->ProcessEvent(ShelloutProcessing::fatalError); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here */ }
|
|
}
|
|
|
|
if (!shelloutProcessing->HadPostScene())
|
|
{
|
|
try { shelloutProcessing->ProcessEvent(ShelloutProcessing::postScene); }
|
|
catch (pov_base::Exception&) { /* Ignore any error here! */ }
|
|
return state = kPostSceneShellout;
|
|
}
|
|
}
|
|
|
|
return state = kDone;
|
|
|
|
case kDone:
|
|
return state = kReady;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
void VirtualFrontEnd::SetResultPointers(Console **cr, Image **ir, Display **dr)
|
|
{
|
|
consoleResult = cr;
|
|
displayResult = dr;
|
|
}
|
|
|
|
bool VirtualFrontEnd::IsPausable (void)
|
|
{
|
|
switch (GetState ())
|
|
{
|
|
case kParsing :
|
|
case kPausedParsing :
|
|
case kRendering :
|
|
case kPausedRendering :
|
|
case kPreSceneShellout:
|
|
case kPreFrameShellout:
|
|
case kPostFrameShellout:
|
|
case kPostShelloutPause:
|
|
return (true) ;
|
|
|
|
default :
|
|
return (false) ;
|
|
}
|
|
}
|
|
|
|
bool VirtualFrontEnd::Paused (void)
|
|
{
|
|
switch (GetState ())
|
|
{
|
|
case kPausedParsing :
|
|
case kPausedRendering :
|
|
case kPostShelloutPause:
|
|
return (true) ;
|
|
|
|
case kPreSceneShellout:
|
|
case kPreFrameShellout:
|
|
case kPostFrameShellout:
|
|
return m_PauseRequested;
|
|
|
|
default :
|
|
return (false) ;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// helper funtions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Allow_File_Write (const char *Filename, const unsigned int FileType)
|
|
{
|
|
if (strcmp(Filename, "stdout") == 0 || strcmp(Filename, "stderr") == 0)
|
|
return true;
|
|
return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, true));
|
|
}
|
|
|
|
int Allow_File_Write (const unsigned short *Filename, const unsigned int FileType)
|
|
{
|
|
if (strcmp(UCS2toASCIIString(Filename).c_str(), "stdout") == 0 || strcmp(UCS2toASCIIString(Filename).c_str(), "stderr") == 0)
|
|
return true;
|
|
return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, true));
|
|
}
|
|
|
|
int Allow_File_Read (const char *Filename, const unsigned int FileType)
|
|
{
|
|
return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, false));
|
|
}
|
|
|
|
int Allow_File_Read (const unsigned short *Filename, const unsigned int FileType)
|
|
{
|
|
return (vfeSession::GetSessionFromThreadID()->TestAccessAllowed(Filename, false));
|
|
}
|
|
|
|
FILE *vfeFOpen (const std::basic_string<unsigned short>& name, const char *mode)
|
|
{
|
|
return (fopen (UCS2toASCIIString (name).c_str(), mode)) ;
|
|
}
|
|
|
|
bool vfeRemove(const UCS2String& Filename)
|
|
{
|
|
return (DELETE_FILE (UCS2toASCIIString (Filename).c_str()) == 0);
|
|
}
|
|
|
|
}
|
|
|