2015-05-24 04:51:16 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <system_error>
|
|
|
|
#include <string>
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
#include <HECL/HECL.hpp>
|
|
|
|
#include <LogVisor/LogVisor.hpp>
|
2015-07-08 04:26:29 +00:00
|
|
|
#include "BlenderConnection.hpp"
|
2015-05-24 04:51:16 +00:00
|
|
|
|
2015-07-28 02:25:33 +00:00
|
|
|
namespace HECL
|
|
|
|
{
|
|
|
|
|
|
|
|
LogVisor::LogModule BlenderLog("BlenderConnection");
|
|
|
|
BlenderConnection* SharedBlenderConnection = nullptr;
|
2015-07-22 19:14:50 +00:00
|
|
|
|
2015-05-24 04:51:16 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender"
|
|
|
|
#elif _WIN32
|
2015-07-22 19:14:50 +00:00
|
|
|
#define DEFAULT_BLENDER_BIN _S("%ProgramFiles%\\Blender Foundation\\Blender\\blender.exe")
|
2015-05-24 04:51:16 +00:00
|
|
|
#else
|
|
|
|
#define DEFAULT_BLENDER_BIN "blender"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define TEMP_SHELLSCRIPT "/home/jacko/hecl/blender/blendershell.py"
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
|
|
|
size_t readBytes = 0;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (readBytes >= bufSz)
|
2015-05-25 01:34:31 +00:00
|
|
|
{
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, "Pipe buffer overrun\n");
|
2015-05-25 01:34:31 +00:00
|
|
|
*(buf-1) = '\0';
|
|
|
|
return bufSz - 1;
|
|
|
|
}
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
DWORD ret = 0;
|
|
|
|
if (!ReadFile(m_readpipe[0], buf, 1, &ret, NULL))
|
|
|
|
goto err;
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
ssize_t ret = read(m_readpipe[0], buf, 1);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
2015-07-22 19:14:50 +00:00
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
else if (ret == 1)
|
|
|
|
{
|
|
|
|
if (*buf == '\n')
|
|
|
|
{
|
|
|
|
*buf = '\0';
|
|
|
|
return readBytes;
|
|
|
|
}
|
|
|
|
++readBytes;
|
|
|
|
++buf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*buf = '\0';
|
|
|
|
return readBytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err:
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
2015-05-24 04:51:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
size_t BlenderConnection::_writeLine(const char* buf)
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
DWORD ret = 0;
|
|
|
|
if (!WriteFile(m_writepipe[1], buf, strlen(buf), &ret, NULL))
|
|
|
|
goto err;
|
|
|
|
if (!WriteFile(m_writepipe[1], "\n", 1, NULL, NULL))
|
|
|
|
goto err;
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
ssize_t ret, nlerr;
|
|
|
|
ret = write(m_writepipe[1], buf, strlen(buf));
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
nlerr = write(m_writepipe[1], "\n", 1);
|
|
|
|
if (nlerr < 0)
|
|
|
|
goto err;
|
2015-07-22 19:14:50 +00:00
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
return (size_t)ret;
|
|
|
|
err:
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
2015-07-26 02:52:02 +00:00
|
|
|
return 0;
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
size_t BlenderConnection::_readBuf(char* buf, size_t len)
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
DWORD ret = 0;
|
|
|
|
if (!ReadFile(m_readpipe[0], buf, len, &ret, NULL))
|
|
|
|
goto err;
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
ssize_t ret = read(m_readpipe[0], buf, len);
|
|
|
|
if (ret < 0)
|
2015-07-22 19:14:50 +00:00
|
|
|
goto err;
|
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
return ret;
|
2015-07-22 19:14:50 +00:00
|
|
|
err:
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
2015-07-22 19:14:50 +00:00
|
|
|
return 0;
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
size_t BlenderConnection::_writeBuf(const char* buf, size_t len)
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
DWORD ret = 0;
|
|
|
|
if (!WriteFile(m_writepipe[1], buf, len, &ret, NULL))
|
|
|
|
goto err;
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
ssize_t ret = write(m_writepipe[1], buf, len);
|
|
|
|
if (ret < 0)
|
2015-07-22 19:14:50 +00:00
|
|
|
goto err;
|
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
return ret;
|
2015-07-22 19:14:50 +00:00
|
|
|
err:
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, strerror(errno));
|
2015-07-22 19:14:50 +00:00
|
|
|
return 0;
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
void BlenderConnection::_closePipe()
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
CloseHandle(m_readpipe[0]);
|
|
|
|
CloseHandle(m_writepipe[1]);
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
close(m_readpipe[0]);
|
|
|
|
close(m_writepipe[1]);
|
2015-07-22 19:14:50 +00:00
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
BlenderConnection::BlenderConnection(bool silenceBlender)
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
|
|
|
/* Construct communication pipes */
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
|
|
|
|
CreatePipe(&m_readpipe[0], &m_readpipe[1], &sattrs, 0);
|
|
|
|
CreatePipe(&m_writepipe[0], &m_writepipe[1], &sattrs, 0);
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
pipe(m_readpipe);
|
|
|
|
pipe(m_writepipe);
|
2015-07-22 19:14:50 +00:00
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
|
|
|
|
/* User-specified blender path */
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
wchar_t BLENDER_BIN_BUF[2048];
|
|
|
|
wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
char* blenderBin = getenv("BLENDER_BIN");
|
2015-07-22 19:14:50 +00:00
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
|
|
|
|
/* Child process of blender */
|
2015-07-22 19:14:50 +00:00
|
|
|
#if _WIN32
|
|
|
|
if (!blenderBin)
|
|
|
|
{
|
|
|
|
/* Environment not set; use registry */
|
|
|
|
HKEY blenderKey;
|
|
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\BlenderFoundation", 0, KEY_READ, &blenderKey) == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
DWORD bufSz = sizeof(BLENDER_BIN_BUF);
|
|
|
|
if (RegGetValueW(blenderKey, NULL, L"Install_Dir", REG_SZ, NULL, BLENDER_BIN_BUF, &bufSz) == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
wcscat_s(BLENDER_BIN_BUF, 2048, L"\\blender.exe");
|
|
|
|
blenderBin = BLENDER_BIN_BUF;
|
|
|
|
}
|
|
|
|
RegCloseKey(blenderKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!blenderBin)
|
|
|
|
{
|
|
|
|
Log.report(LogVisor::FatalError, "unable to find blender");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wchar_t cmdLine[2048];
|
|
|
|
_snwprintf(cmdLine, 2048, L" --background -P shellscript.py -- %08X %08X",
|
|
|
|
(uint32_t)m_writepipe[0], (uint32_t)m_readpipe[1]);
|
|
|
|
|
|
|
|
STARTUPINFO sinfo = {sizeof(STARTUPINFO)};
|
|
|
|
HANDLE nulHandle = NULL;
|
|
|
|
if (silenceBlender)
|
|
|
|
{
|
|
|
|
nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sattrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
sinfo.hStdError = nulHandle;
|
|
|
|
sinfo.hStdOutput = nulHandle;
|
|
|
|
sinfo.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
}
|
|
|
|
|
|
|
|
PROCESS_INFORMATION pinfo;
|
|
|
|
if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
|
|
|
|
Log.report(LogVisor::FatalError, "unable to launch blender");
|
|
|
|
|
|
|
|
CloseHandle(m_writepipe[1]);
|
|
|
|
CloseHandle(m_readpipe[0]);
|
|
|
|
|
|
|
|
if (nulHandle)
|
|
|
|
CloseHandle(nulHandle);
|
|
|
|
|
|
|
|
#else
|
2015-05-24 04:51:16 +00:00
|
|
|
pid_t pid = fork();
|
|
|
|
if (!pid)
|
|
|
|
{
|
|
|
|
close(m_writepipe[1]);
|
|
|
|
close(m_readpipe[0]);
|
|
|
|
|
|
|
|
if (silenceBlender)
|
|
|
|
{
|
|
|
|
close(STDOUT_FILENO);
|
|
|
|
close(STDERR_FILENO);
|
|
|
|
}
|
|
|
|
|
|
|
|
char errbuf[256];
|
|
|
|
char readfds[32];
|
|
|
|
snprintf(readfds, 32, "%d", m_writepipe[0]);
|
|
|
|
char writefds[32];
|
|
|
|
snprintf(writefds, 32, "%d", m_readpipe[1]);
|
|
|
|
|
2015-05-24 22:19:28 +00:00
|
|
|
/* Try user-specified blender first */
|
2015-05-24 04:51:16 +00:00
|
|
|
if (blenderBin)
|
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
execlp(blenderBin, blenderBin,
|
|
|
|
"--background", "-P", TEMP_SHELLSCRIPT,
|
2015-05-24 04:51:16 +00:00
|
|
|
"--", readfds, writefds, NULL);
|
|
|
|
if (errno != ENOENT)
|
|
|
|
{
|
|
|
|
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
|
|
|
|
write(m_writepipe[1], errbuf, strlen(errbuf));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-24 22:19:28 +00:00
|
|
|
/* Otherwise default blender */
|
|
|
|
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
|
|
|
|
"--background", "-P", TEMP_SHELLSCRIPT,
|
2015-05-24 04:51:16 +00:00
|
|
|
"--", readfds, writefds, NULL);
|
|
|
|
if (errno != ENOENT)
|
|
|
|
{
|
|
|
|
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
|
|
|
|
write(m_writepipe[1], errbuf, strlen(errbuf));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unable to find blender */
|
|
|
|
write(m_writepipe[1], "NOBLENDER\n", 10);
|
|
|
|
exit(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
close(m_writepipe[0]);
|
|
|
|
close(m_readpipe[1]);
|
|
|
|
m_blenderProc = pid;
|
2015-07-22 19:14:50 +00:00
|
|
|
#endif
|
2015-05-24 04:51:16 +00:00
|
|
|
|
|
|
|
/* Handle first response */
|
|
|
|
char lineBuf[256];
|
2015-05-24 22:19:28 +00:00
|
|
|
_readLine(lineBuf, sizeof(lineBuf));
|
2015-05-24 04:51:16 +00:00
|
|
|
if (!strcmp(lineBuf, "NOLAUNCH"))
|
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
_closePipe();
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, "Unable to launch blender");
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
else if (!strcmp(lineBuf, "NOBLENDER"))
|
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
_closePipe();
|
2015-05-24 04:51:16 +00:00
|
|
|
if (blenderBin)
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s' or '%s'"),
|
2015-07-22 19:14:50 +00:00
|
|
|
blenderBin, DEFAULT_BLENDER_BIN);
|
2015-05-24 04:51:16 +00:00
|
|
|
else
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"),
|
2015-07-22 19:14:50 +00:00
|
|
|
DEFAULT_BLENDER_BIN);
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
else if (!strcmp(lineBuf, "NOADDON"))
|
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
_closePipe();
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, "HECL addon not installed within blender");
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
else if (strcmp(lineBuf, "READY"))
|
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
_closePipe();
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf);
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
2015-05-24 22:19:28 +00:00
|
|
|
_writeLine("ACK");
|
2015-05-24 04:51:16 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
BlenderConnection::~BlenderConnection()
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
_closePipe();
|
|
|
|
}
|
|
|
|
|
2015-07-28 02:25:33 +00:00
|
|
|
bool BlenderConnection::createBlend(const SystemString& path)
|
|
|
|
{
|
|
|
|
_writeLine(("CREATE" + path).c_str());
|
|
|
|
char lineBuf[256];
|
|
|
|
_readLine(lineBuf, sizeof(lineBuf));
|
|
|
|
if (!strcmp(lineBuf, "FINISHED"))
|
|
|
|
{
|
|
|
|
m_loadedBlend = path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BlenderConnection::openBlend(const SystemString& path)
|
2015-05-24 22:19:28 +00:00
|
|
|
{
|
|
|
|
_writeLine(("OPEN" + path).c_str());
|
|
|
|
char lineBuf[256];
|
|
|
|
_readLine(lineBuf, sizeof(lineBuf));
|
|
|
|
if (!strcmp(lineBuf, "FINISHED"))
|
|
|
|
{
|
|
|
|
m_loadedBlend = path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
bool BlenderConnection::cookBlend(std::function<char*(uint32_t)> bufGetter,
|
2015-05-24 22:19:28 +00:00
|
|
|
const std::string& expectedType,
|
|
|
|
const std::string& platform,
|
|
|
|
bool bigEndian)
|
|
|
|
{
|
|
|
|
char lineBuf[256];
|
|
|
|
char reqLine[512];
|
|
|
|
snprintf(reqLine, 512, "COOK %s %c", platform.c_str(), bigEndian?'>':'<');
|
|
|
|
_writeLine(reqLine);
|
|
|
|
_readLine(lineBuf, sizeof(lineBuf));
|
|
|
|
if (strcmp(expectedType.c_str(), lineBuf))
|
|
|
|
{
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::Error, "expected '%s' to contain '%s' not '%s'",
|
2015-07-26 02:52:02 +00:00
|
|
|
m_loadedBlend.c_str(), expectedType.c_str(), lineBuf);
|
2015-05-24 22:19:28 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
_writeLine("ACK");
|
|
|
|
|
|
|
|
for (_readLine(lineBuf, sizeof(lineBuf));
|
|
|
|
!strcmp("BUF", lineBuf);
|
|
|
|
_readLine(lineBuf, sizeof(lineBuf)))
|
|
|
|
{
|
|
|
|
uint32_t sz;
|
2015-05-26 04:42:20 +00:00
|
|
|
_readBuf((char*)&sz, 4);
|
|
|
|
char* buf = bufGetter(sz);
|
2015-05-24 22:19:28 +00:00
|
|
|
_readBuf(buf, sz);
|
|
|
|
}
|
|
|
|
if (!strcmp("SUCCESS", lineBuf))
|
|
|
|
return true;
|
|
|
|
else if (!strcmp("EXCEPTION", lineBuf))
|
2015-07-28 02:25:33 +00:00
|
|
|
BlenderLog.report(LogVisor::FatalError, "blender script exception");
|
2015-05-24 22:19:28 +00:00
|
|
|
|
|
|
|
return false;
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
void BlenderConnection::quitBlender()
|
2015-05-24 04:51:16 +00:00
|
|
|
{
|
2015-05-24 22:19:28 +00:00
|
|
|
_writeLine("QUIT");
|
2015-05-24 04:51:16 +00:00
|
|
|
char lineBuf[256];
|
2015-05-24 22:19:28 +00:00
|
|
|
_readLine(lineBuf, sizeof(lineBuf));
|
2015-05-24 04:51:16 +00:00
|
|
|
}
|
2015-07-28 02:25:33 +00:00
|
|
|
|
|
|
|
}
|