metaforce/hecl/blender/BlenderConnection.cpp

966 lines
28 KiB
C++
Raw Normal View History

2015-05-24 04:51:16 +00:00
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2015-08-31 19:47:59 +00:00
#include <inttypes.h>
2015-05-24 04:51:16 +00:00
#include <system_error>
#include <string>
2015-10-16 08:02:59 +00:00
#include <algorithm>
2015-05-24 04:51:16 +00:00
2016-03-04 23:02:44 +00:00
#include <hecl/hecl.hpp>
#include <hecl/Database.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-08-31 03:36:24 +00:00
#if _WIN32
#include <io.h>
#include <fcntl.h>
#endif
2015-10-22 02:01:08 +00:00
namespace std
{
template <> struct hash<std::pair<uint32_t,uint32_t>>
{
size_t operator()(const std::pair<uint32_t,uint32_t>& val) const NOEXCEPT
{
/* this will potentially truncate the second value if 32-bit size_t,
* however, its application here is intended to operate in 16-bit indices */
return val.first | (val.second << 16);
}
};
}
2016-03-04 23:02:44 +00:00
namespace hecl
2015-07-28 02:25:33 +00:00
{
2016-03-04 23:02:44 +00:00
logvisor::Module BlenderLog("BlenderConnection");
2016-03-28 22:39:18 +00:00
BlenderToken SharedBlenderToken;
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"
#else
#define DEFAULT_BLENDER_BIN "blender"
#endif
2015-08-13 07:30:23 +00:00
extern "C" uint8_t HECL_BLENDERSHELL[];
extern "C" size_t HECL_BLENDERSHELL_SZ;
2015-05-24 04:51:16 +00:00
2015-08-31 03:36:24 +00:00
extern "C" uint8_t HECL_ADDON[];
extern "C" size_t HECL_ADDON_SZ;
2015-09-06 20:08:23 +00:00
extern "C" uint8_t HECL_STARTUP[];
extern "C" size_t HECL_STARTUP_SZ;
2015-08-31 03:36:24 +00:00
2015-09-06 20:08:23 +00:00
static void InstallBlendershell(const SystemChar* path)
{
2016-03-04 23:02:44 +00:00
FILE* fp = hecl::Fopen(path, _S("w"));
2015-09-06 20:08:23 +00:00
if (!fp)
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, _S("unable to open %s for writing"), path);
2015-09-06 20:08:23 +00:00
fwrite(HECL_BLENDERSHELL, 1, HECL_BLENDERSHELL_SZ, fp);
fclose(fp);
}
static void InstallAddon(const SystemChar* path)
2015-08-31 03:36:24 +00:00
{
2016-03-04 23:02:44 +00:00
FILE* fp = hecl::Fopen(path, _S("wb"));
2015-08-31 03:36:24 +00:00
if (!fp)
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, _S("Unable to install blender addon at '%s'"), path);
2015-08-31 03:36:24 +00:00
fwrite(HECL_ADDON, 1, HECL_ADDON_SZ, fp);
fclose(fp);
}
2015-09-06 20:08:23 +00:00
static void InstallStartup(const char* path)
{
FILE* fp = fopen(path, "wb");
if (!fp)
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "Unable to place hecl_startup.blend at '%s'", path);
2015-09-06 20:08:23 +00:00
fwrite(HECL_STARTUP, 1, HECL_STARTUP_SZ, fp);
fclose(fp);
}
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
{
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "Pipe buffer overrun");
2015-05-25 01:34:31 +00:00
*(buf-1) = '\0';
return bufSz - 1;
}
2015-08-31 03:36:24 +00:00
int ret = read(m_readpipe[0], buf, 1);
2015-05-24 04:51:16 +00:00
if (ret < 0)
2015-10-16 08:02:59 +00:00
{
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, strerror(errno));
2015-10-16 08:02:59 +00:00
return 0;
}
2015-05-24 04:51:16 +00:00
else if (ret == 1)
{
if (*buf == '\n')
{
*buf = '\0';
2015-10-16 08:02:59 +00:00
if (readBytes >= 4)
if (!memcmp(buf, "EXCEPTION", std::min(readBytes, size_t(9))))
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "Blender exception");
2015-05-24 04:51:16 +00:00
return readBytes;
}
++readBytes;
++buf;
}
else
{
*buf = '\0';
2015-10-16 08:02:59 +00:00
if (readBytes >= 4)
if (!memcmp(buf, "EXCEPTION", std::min(readBytes, size_t(9))))
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "Blender exception");
2015-05-24 04:51:16 +00:00
return readBytes;
}
}
}
2015-07-22 19:14:50 +00:00
size_t BlenderConnection::_writeLine(const char* buf)
2015-05-24 04:51:16 +00:00
{
2015-08-31 03:36:24 +00:00
int ret, nlerr;
2015-05-24 04:51:16 +00:00
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;
return (size_t)ret;
err:
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, strerror(errno));
2015-07-26 02:52:02 +00:00
return 0;
2015-05-24 04:51:16 +00:00
}
2015-10-02 04:06:45 +00:00
size_t BlenderConnection::_readBuf(void* buf, size_t len)
2015-05-24 04:51:16 +00:00
{
2015-08-31 03:36:24 +00:00
int ret = read(m_readpipe[0], buf, len);
2015-05-24 04:51:16 +00:00
if (ret < 0)
2015-07-22 19:14:50 +00:00
goto err;
2015-10-16 08:02:59 +00:00
if (len >= 4)
if (!memcmp((char*)buf, "EXCEPTION", std::min(len, size_t(9))))
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "Blender exception");
2015-05-24 04:51:16 +00:00
return ret;
2015-07-22 19:14:50 +00:00
err:
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, strerror(errno));
2015-07-22 19:14:50 +00:00
return 0;
2015-05-24 04:51:16 +00:00
}
2015-10-02 04:06:45 +00:00
size_t BlenderConnection::_writeBuf(const void* buf, size_t len)
2015-05-24 04:51:16 +00:00
{
2015-08-31 03:36:24 +00:00
int ret = write(m_writepipe[1], buf, len);
2015-05-24 04:51:16 +00:00
if (ret < 0)
2015-07-22 19:14:50 +00:00
goto err;
2015-05-24 04:51:16 +00:00
return ret;
2015-07-22 19:14:50 +00:00
err:
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, 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
{
close(m_readpipe[0]);
close(m_writepipe[1]);
}
2015-09-22 01:41:38 +00:00
BlenderConnection::BlenderConnection(int verbosityLevel)
2015-05-24 04:51:16 +00:00
{
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Info, "Establishing BlenderConnection...");
2015-09-02 02:31:33 +00:00
2015-08-13 07:30:23 +00:00
/* Put hecl_blendershell.py in temp dir */
#ifdef _WIN32
wchar_t* TMPDIR = _wgetenv(L"TEMP");
if (!TMPDIR)
TMPDIR = (wchar_t*)L"\\Temp";
2016-03-04 23:02:44 +00:00
m_startupBlend = hecl::WideToUTF8(TMPDIR);
#else
char* TMPDIR = getenv("TMPDIR");
if (!TMPDIR)
TMPDIR = (char*)"/tmp";
2015-09-06 21:00:50 +00:00
m_startupBlend = TMPDIR;
#endif
2016-03-04 23:02:44 +00:00
hecl::SystemString blenderShellPath(TMPDIR);
2015-08-13 07:30:23 +00:00
blenderShellPath += _S("/hecl_blendershell.py");
2015-09-06 20:08:23 +00:00
InstallBlendershell(blenderShellPath.c_str());
2016-03-04 23:02:44 +00:00
hecl::SystemString blenderAddonPath(TMPDIR);
2015-08-31 03:36:24 +00:00
blenderAddonPath += _S("/hecl_blenderaddon.zip");
2015-09-02 02:31:33 +00:00
InstallAddon(blenderAddonPath.c_str());
2015-09-06 20:08:23 +00:00
2015-09-06 21:00:50 +00:00
m_startupBlend += "/hecl_startup.blend";
2015-09-06 20:08:23 +00:00
InstallStartup(m_startupBlend.c_str());
2015-08-31 03:36:24 +00:00
int installAttempt = 0;
while (true)
{
/* Construct communication pipes */
2015-07-22 19:14:50 +00:00
#if _WIN32
2015-08-31 03:36:24 +00:00
_pipe(m_readpipe, 2048, _O_BINARY);
_pipe(m_writepipe, 2048, _O_BINARY);
HANDLE writehandle = HANDLE(_get_osfhandle(m_writepipe[0]));
SetHandleInformation(writehandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
HANDLE readhandle = HANDLE(_get_osfhandle(m_readpipe[1]));
SetHandleInformation(readhandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
2015-07-22 19:14:50 +00:00
#else
2015-08-31 03:36:24 +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
2015-08-31 03:36:24 +00:00
/* User-specified blender path */
2015-07-22 19:14:50 +00:00
#if _WIN32
2015-08-31 03:36:24 +00:00
wchar_t BLENDER_BIN_BUF[2048];
wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
2015-07-22 19:14:50 +00:00
#else
2015-08-31 03:36:24 +00:00
char* blenderBin = getenv("BLENDER_BIN");
2015-07-22 19:14:50 +00:00
#endif
2015-05-24 04:51:16 +00:00
2015-08-31 03:36:24 +00:00
/* Child process of blender */
2015-07-22 19:14:50 +00:00
#if _WIN32
2015-08-31 03:36:24 +00:00
if (!blenderBin)
2015-07-22 19:14:50 +00:00
{
2015-08-31 03:36:24 +00:00
/* Environment not set; use default */
wchar_t progFiles[256];
if (!GetEnvironmentVariableW(L"ProgramFiles", progFiles, 256))
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, L"unable to determine 'Program Files' path");
2015-08-31 03:36:24 +00:00
_snwprintf(BLENDER_BIN_BUF, 2048, L"%s\\Blender Foundation\\Blender\\blender.exe", progFiles);
blenderBin = BLENDER_BIN_BUF;
2015-07-22 19:14:50 +00:00
}
2015-08-31 03:36:24 +00:00
wchar_t cmdLine[2048];
2015-09-22 01:41:38 +00:00
_snwprintf(cmdLine, 2048, L" --background -P \"%s\" -- %" PRIuPTR " %" PRIuPTR " %d \"%s\"",
blenderShellPath.c_str(), uintptr_t(writehandle), uintptr_t(readhandle),
2015-10-22 02:01:08 +00:00
verbosityLevel, blenderAddonPath.c_str());
2015-07-22 19:14:50 +00:00
2015-08-31 03:36:24 +00:00
STARTUPINFO sinfo = {sizeof(STARTUPINFO)};
HANDLE nulHandle = NULL;
2015-09-27 04:35:36 +00:00
if (verbosityLevel == 0)
2015-08-31 03:36:24 +00:00
{
SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
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;
}
2015-07-22 19:14:50 +00:00
2015-08-31 03:36:24 +00:00
PROCESS_INFORMATION pinfo;
if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
{
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, L"unable to launch blender from %s: %s", blenderBin, messageBuffer);
2015-08-31 03:36:24 +00:00
}
2015-07-22 19:14:50 +00:00
2015-08-31 03:36:24 +00:00
close(m_writepipe[0]);
close(m_readpipe[1]);
2015-07-22 19:14:50 +00:00
2015-08-31 03:36:24 +00:00
if (nulHandle)
CloseHandle(nulHandle);
2015-07-22 19:14:50 +00:00
#else
2015-08-31 03:36:24 +00:00
pid_t pid = fork();
if (!pid)
2015-05-24 04:51:16 +00:00
{
2015-08-31 03:36:24 +00:00
close(m_writepipe[1]);
close(m_readpipe[0]);
2015-05-24 04:51:16 +00:00
2015-09-22 01:41:38 +00:00
if (verbosityLevel == 0)
2015-08-31 03:36:24 +00:00
{
int devNull = open("/dev/null", O_WRONLY);
dup2(devNull, STDOUT_FILENO);
dup2(devNull, STDERR_FILENO);
}
2015-05-24 04:51:16 +00:00
2015-08-31 03:36:24 +00:00
char errbuf[256];
char readfds[32];
snprintf(readfds, 32, "%d", m_writepipe[0]);
char writefds[32];
snprintf(writefds, 32, "%d", m_readpipe[1]);
2015-10-22 02:01:08 +00:00
char vLevel[32];
snprintf(vLevel, 32, "%d", verbosityLevel);
2015-08-31 03:36:24 +00:00
/* Try user-specified blender first */
if (blenderBin)
{
2015-09-02 02:31:33 +00:00
execlp(blenderBin, blenderBin,
"--background", "-P", blenderShellPath.c_str(),
2015-10-22 02:01:08 +00:00
"--", readfds, writefds, vLevel, blenderAddonPath.c_str(), NULL);
2015-08-31 03:36:24 +00:00
if (errno != ENOENT)
{
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
write(m_writepipe[1], errbuf, strlen(errbuf));
exit(1);
}
}
/* Otherwise default blender */
2015-09-02 02:31:33 +00:00
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
"--background", "-P", blenderShellPath.c_str(),
2015-10-22 02:01:08 +00:00
"--", readfds, writefds, vLevel, blenderAddonPath.c_str(), NULL);
2015-05-24 04:51:16 +00:00
if (errno != ENOENT)
{
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
write(m_writepipe[1], errbuf, strlen(errbuf));
exit(1);
}
2015-08-31 03:36:24 +00:00
/* Unable to find blender */
write(m_writepipe[1], "NOBLENDER\n", 10);
2015-05-24 04:51:16 +00:00
exit(1);
2015-08-31 03:36:24 +00:00
}
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
2015-08-31 03:36:24 +00:00
/* Handle first response */
char lineBuf[256];
_readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "NOLAUNCH"))
{
_closePipe();
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "Unable to launch blender");
2015-08-31 03:36:24 +00:00
}
else if (!strcmp(lineBuf, "NOBLENDER"))
{
_closePipe();
if (blenderBin)
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, _S("Unable to find blender at '%s' or '%s'"),
2015-08-31 03:36:24 +00:00
blenderBin, DEFAULT_BLENDER_BIN);
else
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, _S("Unable to find blender at '%s'"),
2015-08-31 03:36:24 +00:00
DEFAULT_BLENDER_BIN);
}
else if (!strcmp(lineBuf, "NOADDON"))
{
_closePipe();
InstallAddon(blenderAddonPath.c_str());
++installAttempt;
if (installAttempt >= 2)
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, _S("unable to install blender addon using '%s'"), blenderAddonPath.c_str());
2015-08-31 03:36:24 +00:00
continue;
}
2015-09-17 19:50:01 +00:00
else if (!strcmp(lineBuf, "ADDONINSTALLED"))
{
_closePipe();
blenderAddonPath = _S("SKIPINSTALL");
continue;
}
2015-08-31 03:36:24 +00:00
else if (strcmp(lineBuf, "READY"))
{
_closePipe();
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal, "read '%s' from blender; expected 'READY'", lineBuf);
2015-08-31 03:36:24 +00:00
}
_writeLine("ACK");
2015-05-24 04:51:16 +00:00
2015-08-31 03:36:24 +00:00
break;
}
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-10-04 04:35:18 +00:00
static const char* BlendTypeStrs[] =
2015-10-01 00:40:06 +00:00
{
"NONE",
"MESH",
"ACTOR",
"AREA",
2015-10-24 01:21:58 +00:00
"WORLD",
"MAPAREA",
"MAPUNIVERSE",
2016-01-22 12:17:18 +00:00
"FRAME",
2015-10-04 04:35:18 +00:00
nullptr
2015-10-01 00:40:06 +00:00
};
2015-10-07 01:16:54 +00:00
bool BlenderConnection::createBlend(const ProjectPath& path, BlendType type)
2015-07-28 02:25:33 +00:00
{
2015-08-16 23:01:35 +00:00
if (m_lock)
2015-08-05 22:59:59 +00:00
{
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal,
2015-08-05 22:59:59 +00:00
"BlenderConnection::createBlend() musn't be called with stream active");
return false;
}
2015-11-21 01:13:06 +00:00
_writeLine(("CREATE \"" + path.getAbsolutePathUTF8() + "\" " + BlendTypeStrs[int(type)] + " \"" + m_startupBlend + "\"").c_str());
2015-07-28 02:25:33 +00:00
char lineBuf[256];
_readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "FINISHED"))
{
2015-08-31 03:36:24 +00:00
m_loadedBlend = path;
2015-10-04 05:08:24 +00:00
m_loadedType = type;
2015-07-28 02:25:33 +00:00
return true;
}
return false;
}
2015-10-16 08:02:59 +00:00
bool BlenderConnection::openBlend(const ProjectPath& path, bool force)
2015-05-24 22:19:28 +00:00
{
2015-08-16 23:01:35 +00:00
if (m_lock)
2015-08-05 22:59:59 +00:00
{
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal,
2015-08-05 22:59:59 +00:00
"BlenderConnection::openBlend() musn't be called with stream active");
return false;
}
2015-10-16 08:02:59 +00:00
if (!force && path == m_loadedBlend)
return true;
2015-10-07 01:16:54 +00:00
_writeLine(("OPEN \"" + path.getAbsolutePathUTF8() + "\"").c_str());
2015-05-24 22:19:28 +00:00
char lineBuf[256];
_readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "FINISHED"))
{
2015-08-31 03:36:24 +00:00
m_loadedBlend = path;
2015-10-04 05:08:24 +00:00
_writeLine("GETTYPE");
_readLine(lineBuf, sizeof(lineBuf));
2015-11-21 01:13:06 +00:00
m_loadedType = BlendType::None;
2015-10-04 05:08:24 +00:00
unsigned idx = 0;
while (BlendTypeStrs[idx])
{
if (!strcmp(BlendTypeStrs[idx], lineBuf))
{
m_loadedType = BlendType(idx);
break;
}
++idx;
}
2015-05-24 22:19:28 +00:00
return true;
}
return false;
}
bool BlenderConnection::saveBlend()
{
2015-08-16 23:01:35 +00:00
if (m_lock)
2015-08-05 22:59:59 +00:00
{
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Fatal,
2015-08-05 22:59:59 +00:00
"BlenderConnection::saveBlend() musn't be called with stream active");
return false;
}
_writeLine("SAVE");
char lineBuf[256];
_readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "FINISHED"))
return true;
return false;
}
2015-08-05 22:59:59 +00:00
void BlenderConnection::deleteBlend()
{
2015-10-07 01:16:54 +00:00
if (m_loadedBlend)
2015-08-05 22:59:59 +00:00
{
2016-03-04 23:02:44 +00:00
hecl::Unlink(m_loadedBlend.getAbsolutePath().c_str());
BlenderLog.report(logvisor::Info, _S("Deleted '%s'"), m_loadedBlend.getAbsolutePath().c_str());
2015-10-07 01:16:54 +00:00
m_loadedBlend = ProjectPath();
2015-08-05 22:59:59 +00:00
}
}
2015-10-24 01:21:58 +00:00
void BlenderConnection::PyOutStream::linkBlend(const char* target,
const char* objName,
bool link)
{
format("if '%s' not in bpy.data.scenes:\n"
2015-08-08 04:52:20 +00:00
" with bpy.data.libraries.load('''%s''', link=%s, relative=True) as (data_from, data_to):\n"
" data_to.scenes = data_from.scenes\n"
" obj_scene = None\n"
" for scene in data_to.scenes:\n"
" if scene.name == '%s':\n"
" obj_scene = scene\n"
" break\n"
2015-08-13 07:30:23 +00:00
" if not obj_scene:\n"
2015-08-15 04:12:57 +00:00
" raise RuntimeError('''unable to find %s in %s. try deleting it and restart the extract.''')\n"
" obj = None\n"
" for object in obj_scene.objects:\n"
" if object.name == obj_scene.name:\n"
" obj = object\n"
"else:\n"
" obj = bpy.data.objects['%s']\n"
"\n",
2015-10-24 01:21:58 +00:00
objName, target, link?"True":"False",
objName, objName, target, objName);
}
void BlenderConnection::PyOutStream::linkBackground(const char* target,
const char* sceneName)
{
format("if '%s' not in bpy.data.scenes:\n"
" with bpy.data.libraries.load('''%s''', link=True, relative=True) as (data_from, data_to):\n"
" data_to.scenes = data_from.scenes\n"
" obj_scene = None\n"
" for scene in data_to.scenes:\n"
" if scene.name == '%s':\n"
" obj_scene = scene\n"
" break\n"
" if not obj_scene:\n"
" raise RuntimeError('''unable to find %s in %s. try deleting it and restart the extract.''')\n"
"\n"
"bpy.context.scene.background_set = bpy.data.scenes['%s']\n",
sceneName, target,
sceneName, sceneName, target, sceneName);
}
2015-10-22 02:01:08 +00:00
BlenderConnection::DataStream::Mesh::Mesh
(BlenderConnection& conn, HMDLTopology topologyIn, int skinSlotCount, SurfProgFunc& surfProg)
: topology(topologyIn), aabbMin(conn), aabbMax(conn)
2015-10-02 04:06:45 +00:00
{
2015-10-03 01:53:45 +00:00
uint32_t matSetCount;
conn._readBuf(&matSetCount, 4);
materialSets.reserve(matSetCount);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<matSetCount ; ++i)
2015-10-02 04:06:45 +00:00
{
2015-10-03 01:53:45 +00:00
materialSets.emplace_back();
std::vector<Material>& materials = materialSets.back();
uint32_t matCount;
conn._readBuf(&matCount, 4);
materials.reserve(matCount);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<matCount ; ++i)
2015-10-03 01:53:45 +00:00
materials.emplace_back(conn);
2015-10-02 04:06:45 +00:00
}
uint32_t count;
conn._readBuf(&count, 4);
pos.reserve(count);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<count ; ++i)
2015-10-02 04:06:45 +00:00
pos.emplace_back(conn);
conn._readBuf(&count, 4);
norm.reserve(count);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<count ; ++i)
2015-10-02 04:06:45 +00:00
norm.emplace_back(conn);
conn._readBuf(&colorLayerCount, 4);
2015-10-04 05:08:24 +00:00
if (colorLayerCount > 4)
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "mesh has %u color-layers; max 4", colorLayerCount);
2015-10-04 04:35:18 +00:00
conn._readBuf(&count, 4);
color.reserve(count);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<count ; ++i)
2015-10-04 04:35:18 +00:00
color.emplace_back(conn);
2015-10-02 04:06:45 +00:00
conn._readBuf(&uvLayerCount, 4);
2015-10-04 05:08:24 +00:00
if (uvLayerCount > 8)
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "mesh has %u UV-layers; max 8", uvLayerCount);
2015-10-04 04:35:18 +00:00
conn._readBuf(&count, 4);
uv.reserve(count);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<count ; ++i)
2015-10-04 04:35:18 +00:00
uv.emplace_back(conn);
2015-10-02 04:06:45 +00:00
conn._readBuf(&count, 4);
boneNames.reserve(count);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<count ; ++i)
2015-10-02 04:06:45 +00:00
{
char name[128];
conn._readLine(name, 128);
boneNames.emplace_back(name);
}
conn._readBuf(&count, 4);
skins.reserve(count);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<count ; ++i)
2015-10-02 04:06:45 +00:00
{
skins.emplace_back();
std::vector<SkinBind>& binds = skins.back();
uint32_t bindCount;
conn._readBuf(&bindCount, 4);
binds.reserve(bindCount);
2015-10-12 04:38:49 +00:00
for (uint32_t j=0 ; j<bindCount ; ++j)
2015-10-02 04:06:45 +00:00
binds.emplace_back(conn);
}
2015-10-04 04:35:18 +00:00
/* Assume 16 islands per material for reserve */
if (materialSets.size())
surfaces.reserve(materialSets.front().size() * 16);
2015-10-03 01:53:45 +00:00
uint8_t isSurf;
conn._readBuf(&isSurf, 1);
2015-10-22 02:01:08 +00:00
int prog = 0;
2015-10-03 01:53:45 +00:00
while (isSurf)
2015-10-02 04:06:45 +00:00
{
2015-10-04 04:35:18 +00:00
surfaces.emplace_back(conn, *this, skinSlotCount);
2015-10-22 02:01:08 +00:00
surfProg(++prog);
2015-10-03 01:53:45 +00:00
conn._readBuf(&isSurf, 1);
2015-10-02 04:06:45 +00:00
}
2015-10-04 04:35:18 +00:00
/* Connect skinned verts to bank slots */
2015-10-03 01:53:45 +00:00
if (boneNames.size())
2015-10-04 04:35:18 +00:00
{
2015-10-03 01:53:45 +00:00
for (Surface& surf : surfaces)
2015-10-04 04:35:18 +00:00
{
SkinBanks::Bank& bank = skinBanks.banks[surf.skinBankIdx];
2015-10-04 04:35:18 +00:00
for (Surface::Vert& vert : surf.verts)
{
for (uint32_t i=0 ; i<bank.m_skinIdxs.size() ; ++i)
2015-10-04 04:35:18 +00:00
{
if (bank.m_skinIdxs[i] == vert.iSkin)
2015-10-04 04:35:18 +00:00
{
vert.iBankSkin = i;
break;
}
}
}
}
}
2015-10-02 04:06:45 +00:00
}
2015-10-22 02:01:08 +00:00
BlenderConnection::DataStream::Mesh
BlenderConnection::DataStream::Mesh::getContiguousSkinningVersion() const
{
Mesh newMesh = *this;
newMesh.pos.clear();
newMesh.norm.clear();
2015-10-23 00:44:37 +00:00
newMesh.contiguousSkinVertCounts.clear();
2015-10-22 02:01:08 +00:00
newMesh.contiguousSkinVertCounts.reserve(skins.size());
for (size_t i=0 ; i<skins.size() ; ++i)
{
std::unordered_map<std::pair<uint32_t,uint32_t>, uint32_t> contigMap;
size_t vertCount = 0;
for (Surface& surf : newMesh.surfaces)
{
for (Surface::Vert& vert : surf.verts)
{
if (vert.iSkin == i)
{
auto key = std::make_pair(vert.iPos, vert.iNorm);
auto search = contigMap.find(key);
if (search != contigMap.end())
{
vert.iPos = search->second;
vert.iNorm = search->second;
}
else
{
uint32_t newIdx = newMesh.pos.size();
contigMap[key] = newIdx;
newMesh.pos.push_back(pos.at(vert.iPos));
newMesh.norm.push_back(norm.at(vert.iNorm));
vert.iPos = newIdx;
vert.iNorm = newIdx;
++vertCount;
}
}
}
}
newMesh.contiguousSkinVertCounts.push_back(vertCount);
}
return newMesh;
}
2015-10-03 01:53:45 +00:00
BlenderConnection::DataStream::Mesh::Material::Material
(BlenderConnection& conn)
{
2015-10-14 23:06:47 +00:00
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
conn._readBuf(&bufSz, 4);
source.assign(bufSz, ' ');
conn._readBuf(&source[0], bufSz);
2015-10-03 01:53:45 +00:00
uint32_t texCount;
conn._readBuf(&texCount, 4);
texs.reserve(texCount);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<texCount ; ++i)
2015-10-03 01:53:45 +00:00
{
2015-10-14 23:06:47 +00:00
conn._readBuf(&bufSz, 4);
std::string readStr(bufSz, ' ');
conn._readBuf(&readStr[0], bufSz);
SystemStringView absolute(readStr);
2015-10-07 01:16:54 +00:00
SystemString relative =
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute);
texs.emplace_back(conn.m_loadedBlend.getProject().getProjectWorkingPath(), relative);
2015-10-03 01:53:45 +00:00
}
2015-10-17 02:06:27 +00:00
uint32_t iPropCount;
conn._readBuf(&iPropCount, 4);
iprops.reserve(iPropCount);
for (uint32_t i=0 ; i<iPropCount ; ++i)
{
conn._readBuf(&bufSz, 4);
std::string readStr(bufSz, ' ');
conn._readBuf(&readStr[0], bufSz);
int32_t val;
conn._readBuf(&val, 4);
iprops[readStr] = val;
}
2015-10-03 01:53:45 +00:00
}
BlenderConnection::DataStream::Mesh::Surface::Surface
2015-10-04 04:35:18 +00:00
(BlenderConnection& conn, Mesh& parent, int skinSlotCount)
2015-10-02 04:06:45 +00:00
: centroid(conn), materialIdx(conn), aabbMin(conn), aabbMax(conn),
2015-10-03 01:53:45 +00:00
reflectionNormal(conn)
2015-10-02 04:06:45 +00:00
{
2015-10-04 04:35:18 +00:00
uint32_t countEstimate;
conn._readBuf(&countEstimate, 4);
verts.reserve(countEstimate);
2015-10-03 01:53:45 +00:00
uint8_t isVert;
conn._readBuf(&isVert, 1);
while (isVert)
{
2015-10-02 04:06:45 +00:00
verts.emplace_back(conn, parent);
2015-10-03 01:53:45 +00:00
conn._readBuf(&isVert, 1);
}
2015-10-04 04:35:18 +00:00
if (parent.boneNames.size())
skinBankIdx = parent.skinBanks.addSurface(parent, *this, skinSlotCount);
2015-10-02 04:06:45 +00:00
}
2015-10-03 01:53:45 +00:00
BlenderConnection::DataStream::Mesh::Surface::Vert::Vert
(BlenderConnection& conn, const Mesh& parent)
2015-10-02 04:06:45 +00:00
{
conn._readBuf(&iPos, 4);
conn._readBuf(&iNorm, 4);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<parent.colorLayerCount ; ++i)
2015-10-04 05:08:24 +00:00
conn._readBuf(&iColor[i], 4);
2015-10-12 04:38:49 +00:00
for (uint32_t i=0 ; i<parent.uvLayerCount ; ++i)
2015-10-04 05:08:24 +00:00
conn._readBuf(&iUv[i], 4);
2015-10-02 04:06:45 +00:00
conn._readBuf(&iSkin, 4);
2015-10-04 04:35:18 +00:00
}
static bool VertInBank(const std::vector<uint32_t>& bank, uint32_t sIdx)
{
for (uint32_t idx : bank)
if (sIdx == idx)
return true;
return false;
}
uint32_t BlenderConnection::DataStream::Mesh::SkinBanks::addSurface
(const Mesh& mesh, const Surface& surf, int skinSlotCount)
2015-10-04 04:35:18 +00:00
{
if (banks.empty())
addSkinBank(skinSlotCount);
std::vector<uint32_t> toAdd;
if (skinSlotCount > 0)
toAdd.reserve(skinSlotCount);
std::vector<Bank>::iterator bankIt = banks.begin();
2015-10-04 04:35:18 +00:00
for (;;)
{
bool done = true;
for (; bankIt != banks.end() ; ++bankIt)
{
Bank& bank = *bankIt;
2015-10-04 04:35:18 +00:00
done = true;
for (const Surface::Vert& v : surf.verts)
{
if (!VertInBank(bank.m_skinIdxs, v.iSkin) && !VertInBank(toAdd, v.iSkin))
2015-10-04 04:35:18 +00:00
{
toAdd.push_back(v.iSkin);
if (skinSlotCount > 0 && bank.m_skinIdxs.size() + toAdd.size() > skinSlotCount)
2015-10-04 04:35:18 +00:00
{
toAdd.clear();
done = false;
break;
}
}
}
if (toAdd.size())
{
bank.addSkins(mesh, toAdd);
2015-10-04 04:35:18 +00:00
toAdd.clear();
}
if (done)
return uint32_t(bankIt - banks.begin());
}
if (!done)
{
bankIt = addSkinBank(skinSlotCount);
continue;
}
break;
}
return uint32_t(-1);
2015-10-02 04:06:45 +00:00
}
2015-10-23 00:44:37 +00:00
BlenderConnection::DataStream::Actor::Actor(BlenderConnection& conn)
{
uint32_t armCount;
conn._readBuf(&armCount, 4);
armatures.reserve(armCount);
for (uint32_t i=0 ; i<armCount ; ++i)
armatures.emplace_back(conn);
uint32_t subtypeCount;
conn._readBuf(&subtypeCount, 4);
subtypes.reserve(subtypeCount);
for (uint32_t i=0 ; i<subtypeCount ; ++i)
subtypes.emplace_back(conn);
uint32_t actionCount;
conn._readBuf(&actionCount, 4);
actions.reserve(actionCount);
for (uint32_t i=0 ; i<actionCount ; ++i)
actions.emplace_back(conn);
}
BlenderConnection::DataStream::Actor::Armature::Armature(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
uint32_t boneCount;
conn._readBuf(&boneCount, 4);
bones.reserve(boneCount);
for (uint32_t i=0 ; i<boneCount ; ++i)
bones.emplace_back(conn);
}
BlenderConnection::DataStream::Actor::Armature::Bone::Bone(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
origin.read(conn);
conn._readBuf(&parent, 4);
uint32_t childCount;
conn._readBuf(&childCount, 4);
children.reserve(childCount);
for (uint32_t i=0 ; i<childCount ; ++i)
{
children.emplace_back(0);
conn._readBuf(&children.back(), 4);
}
}
BlenderConnection::DataStream::Actor::Subtype::Subtype(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
std::string meshPath;
conn._readBuf(&bufSz, 4);
if (bufSz)
{
meshPath.assign(bufSz, ' ');
conn._readBuf(&meshPath[0], bufSz);
SystemStringView meshPathAbs(meshPath);
SystemString meshPathRel =
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(meshPathAbs);
mesh.assign(conn.m_loadedBlend.getProject().getProjectWorkingPath(), meshPathRel);
}
conn._readBuf(&armature, 4);
uint32_t overlayCount;
conn._readBuf(&overlayCount, 4);
overlayMeshes.reserve(overlayCount);
for (uint32_t i=0 ; i<overlayCount ; ++i)
{
std::string overlayName;
conn._readBuf(&bufSz, 4);
overlayName.assign(bufSz, ' ');
conn._readBuf(&overlayName[0], bufSz);
std::string meshPath;
conn._readBuf(&bufSz, 4);
if (bufSz)
{
meshPath.assign(bufSz, ' ');
conn._readBuf(&meshPath[0], bufSz);
SystemStringView meshPathAbs(meshPath);
SystemString meshPathRel =
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(meshPathAbs);
overlayMeshes.emplace_back(std::move(overlayName),
ProjectPath(conn.m_loadedBlend.getProject().getProjectWorkingPath(), meshPathRel));
}
}
}
BlenderConnection::DataStream::Actor::Action::Action(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
conn._readBuf(&interval, 4);
conn._readBuf(&additive, 1);
uint32_t frameCount;
conn._readBuf(&frameCount, 4);
frames.reserve(frameCount);
for (uint32_t i=0 ; i<frameCount ; ++i)
{
frames.emplace_back();
conn._readBuf(&frames.back(), 4);
}
uint32_t chanCount;
conn._readBuf(&chanCount, 4);
channels.reserve(chanCount);
for (uint32_t i=0 ; i<chanCount ; ++i)
channels.emplace_back(conn);
uint32_t aabbCount;
conn._readBuf(&aabbCount, 4);
subtypeAABBs.reserve(aabbCount);
for (uint32_t i=0 ; i<aabbCount ; ++i)
{
subtypeAABBs.emplace_back();
subtypeAABBs.back().first.read(conn);
subtypeAABBs.back().second.read(conn);
}
}
BlenderConnection::DataStream::Actor::Action::Channel::Channel(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
boneName.assign(bufSz, ' ');
conn._readBuf(&boneName[0], bufSz);
conn._readBuf(&attrMask, 4);
uint32_t keyCount;
conn._readBuf(&keyCount, 4);
keys.reserve(keyCount);
for (uint32_t i=0 ; i<keyCount ; ++i)
keys.emplace_back(conn, attrMask);
}
BlenderConnection::DataStream::Actor::Action::Channel::Key::Key(BlenderConnection& conn, uint32_t attrMask)
{
if (attrMask & 1)
rotation.read(conn);
if (attrMask & 2)
position.read(conn);
if (attrMask & 4)
scale.read(conn);
}
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
2016-03-28 22:39:18 +00:00
BlenderConnection& BlenderConnection::SharedConnection()
{
return SharedBlenderToken.getBlenderConnection();
}
void BlenderConnection::Shutdown()
{
SharedBlenderToken.shutdown();
}
2015-07-28 02:25:33 +00:00
}