metaforce/hecl/lib/Blender/Connection.cpp

2412 lines
70 KiB
C++
Raw Normal View History

2017-12-29 07:56:31 +00:00
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cinttypes>
2016-08-02 22:12:49 +00:00
#include <signal.h>
2015-05-24 04:51:16 +00:00
#include <system_error>
#include <string>
2015-10-16 08:02:59 +00:00
#include <algorithm>
2016-08-02 22:12:49 +00:00
#include <chrono>
#include <thread>
#include <mutex>
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"
2017-12-29 07:56:31 +00:00
#include "hecl/Blender/Connection.hpp"
#include "hecl/SteamFinder.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
2017-12-30 01:07:15 +00:00
#undef min
#undef max
2015-10-22 02:01:08 +00:00
namespace std
{
template <> struct hash<std::pair<uint32_t,uint32_t>>
{
2017-12-29 07:56:31 +00:00
size_t operator()(const std::pair<uint32_t,uint32_t>& val) const noexcept
2015-10-22 02:01:08 +00:00
{
/* 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);
}
};
}
2017-11-13 06:13:53 +00:00
using namespace std::literals;
2017-12-29 07:56:31 +00:00
namespace hecl::blender
2015-07-28 02:25:33 +00:00
{
2017-12-29 07:56:31 +00:00
logvisor::Module BlenderLog("hecl::blender::Connection");
Token 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;
2016-04-03 03:31:50 +00:00
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);
}
2016-04-03 03:31:50 +00:00
2015-09-06 20:08:23 +00:00
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);
}
2016-04-03 03:31:50 +00:00
static int Read(int fd, void* buf, size_t size)
{
int intrCount = 0;
do
{
auto ret = read(fd, buf, size);
if (ret < 0)
{
if (errno == EINTR)
++intrCount;
else
return -1;
}
else
return ret;
2017-10-27 10:10:08 +00:00
} while (intrCount < 1000);
return -1;
}
static int Write(int fd, const void* buf, size_t size)
{
int intrCount = 0;
do
{
auto ret = write(fd, buf, size);
if (ret < 0)
{
if (errno == EINTR)
++intrCount;
else
return -1;
}
else
return ret;
2017-10-27 10:10:08 +00:00
} while (intrCount < 1000);
return -1;
}
2017-12-29 07:56:31 +00:00
uint32_t Connection::_readStr(char* buf, uint32_t bufSz)
2015-05-24 04:51:16 +00:00
{
uint32_t readLen;
int ret = Read(m_readpipe[0], &readLen, 4);
if (ret < 4)
2015-05-24 04:51:16 +00:00
{
2017-10-27 10:10:08 +00:00
BlenderLog.report(logvisor::Error, "Pipe error %d %s", ret, strerror(errno));
_blenderDied();
return 0;
}
if (readLen >= bufSz)
{
BlenderLog.report(logvisor::Fatal, "Pipe buffer overrun [%d/%d]", readLen, bufSz);
*buf = '\0';
return 0;
}
ret = Read(m_readpipe[0], buf, readLen);
if (ret < 0)
{
BlenderLog.report(logvisor::Fatal, strerror(errno));
return 0;
}
else if (readLen >= 4)
2017-01-19 09:01:54 +00:00
{
if (!memcmp(buf, "EXCEPTION", std::min(readLen, uint32_t(9))))
2015-10-16 08:02:59 +00:00
{
_blenderDied();
2015-10-16 08:02:59 +00:00
return 0;
}
2017-01-19 09:01:54 +00:00
}
*(buf+readLen) = '\0';
return readLen;
2015-05-24 04:51:16 +00:00
}
2017-12-29 07:56:31 +00:00
uint32_t Connection::_writeStr(const char* buf, uint32_t len, int wpipe)
2015-05-24 04:51:16 +00:00
{
2015-08-31 03:36:24 +00:00
int ret, nlerr;
nlerr = Write(wpipe, &len, 4);
if (nlerr < 4)
2015-05-24 04:51:16 +00:00
goto err;
ret = Write(wpipe, buf, len);
if (ret < 0)
2015-05-24 04:51:16 +00:00
goto err;
return (uint32_t)ret;
2015-05-24 04:51:16 +00:00
err:
2016-08-02 22:12:49 +00:00
_blenderDied();
2015-07-26 02:52:02 +00:00
return 0;
2015-05-24 04:51:16 +00:00
}
2017-12-29 07:56:31 +00:00
size_t Connection::_readBuf(void* buf, size_t len)
2015-05-24 04:51:16 +00:00
{
2018-02-26 10:38:50 +00:00
uint8_t* cBuf = reinterpret_cast<uint8_t*>(buf);
size_t readLen = 0;
do
{
int ret = Read(m_readpipe[0], cBuf, len);
if (ret < 0)
goto err;
if (len >= 4)
if (!memcmp((char*) cBuf, "EXCEPTION", std::min(len, size_t(9))))
_blenderDied();
readLen += ret;
cBuf += ret;
len -= ret;
} while (len);
return readLen;
2015-07-22 19:14:50 +00:00
err:
2016-08-02 22:12:49 +00:00
_blenderDied();
2015-07-22 19:14:50 +00:00
return 0;
2015-05-24 04:51:16 +00:00
}
2017-12-29 07:56:31 +00:00
size_t Connection::_writeBuf(const void* buf, size_t len)
2015-05-24 04:51:16 +00:00
{
2018-02-26 10:38:50 +00:00
const uint8_t* cBuf = reinterpret_cast<const uint8_t*>(buf);
size_t writeLen = 0;
do
{
int ret = Write(m_writepipe[1], cBuf, len);
if (ret < 0)
goto err;
writeLen += ret;
cBuf += ret;
len -= ret;
} while (len);
return writeLen;
2015-07-22 19:14:50 +00:00
err:
2016-08-02 22:12:49 +00:00
_blenderDied();
2015-07-22 19:14:50 +00:00
return 0;
2015-05-24 04:51:16 +00:00
}
2017-12-29 07:56:31 +00:00
void Connection::_closePipe()
2015-05-24 04:51:16 +00:00
{
close(m_readpipe[0]);
close(m_writepipe[1]);
#ifdef _WIN32
CloseHandle(m_pinfo.hProcess);
CloseHandle(m_pinfo.hThread);
2016-09-11 21:16:16 +00:00
m_consoleThreadRunning = false;
if (m_consoleThread.joinable())
m_consoleThread.join();
#endif
2015-05-24 04:51:16 +00:00
}
2017-12-29 07:56:31 +00:00
void Connection::_blenderDied()
2016-08-02 22:12:49 +00:00
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
FILE* errFp = hecl::Fopen(m_errPath.c_str(), _S("r"));
if (errFp)
{
fseek(errFp, 0, SEEK_END);
int64_t len = hecl::FTell(errFp);
if (len)
{
fseek(errFp, 0, SEEK_SET);
std::unique_ptr<char[]> buf(new char[len+1]);
memset(buf.get(), 0, len+1);
fread(buf.get(), 1, len, errFp);
2016-12-25 07:02:34 +00:00
BlenderLog.report(logvisor::Fatal, "\n%.*s", int(len), buf.get());
2016-08-02 22:12:49 +00:00
}
}
BlenderLog.report(logvisor::Fatal, "Blender Exception");
}
static std::atomic_bool BlenderFirstInit(false);
static bool RegFileExists(const hecl::SystemChar* path)
{
2017-08-18 03:18:52 +00:00
if (!path)
return false;
hecl::Sstat theStat;
return !hecl::Stat(path, &theStat) && S_ISREG(theStat.st_mode);
}
2017-12-29 07:56:31 +00:00
Connection::Connection(int verbosityLevel)
2015-05-24 04:51:16 +00:00
{
2017-12-06 03:22:31 +00:00
#if !WINDOWS_STORE
2016-03-04 23:02:44 +00:00
BlenderLog.report(logvisor::Info, "Establishing BlenderConnection...");
2016-04-03 03:31:50 +00:00
2015-08-13 07:30:23 +00:00
/* Put hecl_blendershell.py in temp dir */
2017-02-24 08:27:07 +00:00
const SystemChar* TMPDIR = GetTmpDir();
#ifdef _WIN32
2016-03-04 23:02:44 +00:00
m_startupBlend = hecl::WideToUTF8(TMPDIR);
#else
2016-08-03 23:15:59 +00:00
signal(SIGPIPE, SIG_IGN);
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");
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-06 21:00:50 +00:00
m_startupBlend += "/hecl_startup.blend";
bool FalseCmp = false;
if (BlenderFirstInit.compare_exchange_strong(FalseCmp, true))
{
InstallBlendershell(blenderShellPath.c_str());
InstallAddon(blenderAddonPath.c_str());
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);
2016-09-11 21:16:16 +00:00
SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
HANDLE consoleOutReadTmp, consoleOutWrite, consoleErrWrite, consoleOutRead;
if (!CreatePipe(&consoleOutReadTmp, &consoleOutWrite, &sattrs, 0))
BlenderLog.report(logvisor::Fatal, "Error with CreatePipe");
if (!DuplicateHandle(GetCurrentProcess(), consoleOutWrite,
GetCurrentProcess(), &consoleErrWrite, 0,
TRUE,DUPLICATE_SAME_ACCESS))
BlenderLog.report(logvisor::Fatal, "Error with DuplicateHandle");
if (!DuplicateHandle(GetCurrentProcess(), consoleOutReadTmp,
GetCurrentProcess(),
&consoleOutRead, // Address of new handle.
0, FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
BlenderLog.report(logvisor::Fatal, "Error with DupliateHandle");
if (!CloseHandle(consoleOutReadTmp))
BlenderLog.report(logvisor::Fatal, "Error with CloseHandle");
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];
const wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
2015-07-22 19:14:50 +00:00
#else
const char* blenderBin = getenv("BLENDER_BIN");
2015-07-22 19:14:50 +00:00
#endif
2015-05-24 04:51:16 +00:00
/* Steam blender */
hecl::SystemString steamBlender;
2015-08-31 03:36:24 +00:00
/* Child process of blender */
2015-07-22 19:14:50 +00:00
#if _WIN32
if (!blenderBin || !RegFileExists(blenderBin))
2015-07-22 19:14:50 +00:00
{
/* Environment not set; try steam */
steamBlender = hecl::FindCommonSteamApp(_S("Blender"));
if (steamBlender.size())
{
steamBlender += _S("\\blender.exe");
blenderBin = steamBlender.c_str();
}
if (!RegFileExists(blenderBin))
{
/* No steam; try default */
wchar_t progFiles[256];
if (!GetEnvironmentVariableW(L"ProgramFiles", progFiles, 256))
BlenderLog.report(logvisor::Fatal, L"unable to determine 'Program Files' path");
_snwprintf(BLENDER_BIN_BUF, 2048, L"%s\\Blender Foundation\\Blender\\blender.exe", progFiles);
blenderBin = BLENDER_BIN_BUF;
if (!RegFileExists(blenderBin))
BlenderLog.report(logvisor::Fatal, L"unable to find blender.exe");
}
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)};
2016-09-11 21:16:16 +00:00
HANDLE nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
&sattrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
sinfo.dwFlags = STARTF_USESTDHANDLES;
sinfo.hStdInput = nulHandle;
2015-09-27 04:35:36 +00:00
if (verbosityLevel == 0)
2015-08-31 03:36:24 +00:00
{
sinfo.hStdError = nulHandle;
sinfo.hStdOutput = nulHandle;
2016-09-11 21:16:16 +00:00
}
else
{
sinfo.hStdError = consoleErrWrite;
sinfo.hStdOutput = consoleOutWrite;
2015-08-31 03:36:24 +00:00
}
2015-07-22 19:14:50 +00:00
if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &m_pinfo))
2015-08-31 03:36:24 +00:00
{
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
2016-09-11 21:16:16 +00:00
CloseHandle(nulHandle);
CloseHandle(consoleErrWrite);
CloseHandle(consoleOutWrite);
m_consoleThreadRunning = true;
m_consoleThread = std::thread([=]()
2016-09-11 21:16:16 +00:00
{
CHAR lpBuffer[256];
DWORD nBytesRead;
DWORD nCharsWritten;
while (m_consoleThreadRunning)
{
if (!ReadFile(consoleOutRead, lpBuffer, sizeof(lpBuffer),
&nBytesRead, NULL) || !nBytesRead)
{
2016-12-25 22:50:11 +00:00
DWORD err = GetLastError();
if (err == ERROR_BROKEN_PIPE)
break; // pipe done - normal exit path.
else
BlenderLog.report(logvisor::Error, "Error with ReadFile: %08X", err); // Something bad happened.
2016-09-11 21:16:16 +00:00
}
// Display the character read on the screen.
auto lk = logvisor::LockLog();
if (!WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), lpBuffer,
nBytesRead, &nCharsWritten, NULL))
2018-01-21 22:03:48 +00:00
{
//BlenderLog.report(logvisor::Error, "Error with WriteConsole: %08X", GetLastError());
}
2016-09-11 21:16:16 +00:00
}
CloseHandle(consoleOutRead);
});
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);
close(devNull);
2015-08-31 03:36:24 +00:00
}
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", strerror(errno));
_writeStr(errbuf, strlen(errbuf), m_readpipe[1]);
2015-08-31 03:36:24 +00:00
exit(1);
}
}
2017-07-23 23:44:17 +00:00
/* Try steam */
steamBlender = hecl::FindCommonSteamApp(_S("Blender"));
if (steamBlender.size())
{
#ifdef __APPLE__
steamBlender += "/blender.app/Contents/MacOS/blender";
#else
steamBlender += "/blender";
#endif
blenderBin = steamBlender.c_str();
execlp(blenderBin, blenderBin,
"--background", "-P", blenderShellPath.c_str(),
"--", readfds, writefds, vLevel, blenderAddonPath.c_str(), NULL);
if (errno != ENOENT)
{
snprintf(errbuf, 256, "NOLAUNCH %s", strerror(errno));
_writeStr(errbuf, strlen(errbuf), m_readpipe[1]);
exit(1);
}
}
2015-08-31 03:36:24 +00:00
/* 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", strerror(errno));
_writeStr(errbuf, strlen(errbuf), m_readpipe[1]);
2015-05-24 04:51:16 +00:00
exit(1);
}
2015-08-31 03:36:24 +00:00
/* Unable to find blender */
_writeStr("NOBLENDER", 9, m_readpipe[1]);
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
2016-09-11 21:16:16 +00:00
/* Stash error path and unlink existing file */
2016-12-26 05:54:23 +00:00
#if _WIN32
2016-12-25 07:02:34 +00:00
m_errPath = hecl::SystemString(TMPDIR) + hecl::SysFormat(_S("/hecl_%016llX.derp"),
(unsigned long long)m_pinfo.dwProcessId);
2016-12-26 05:54:23 +00:00
#else
m_errPath = hecl::SystemString(TMPDIR) + hecl::SysFormat(_S("/hecl_%016llX.derp"),
(unsigned long long)m_blenderProc);
#endif
2016-08-02 22:12:49 +00:00
hecl::Unlink(m_errPath.c_str());
2015-08-31 03:36:24 +00:00
/* Handle first response */
char lineBuf[256];
_readStr(lineBuf, sizeof(lineBuf));
if (!strncmp(lineBuf, "NOLAUNCH", 8))
2015-08-31 03:36:24 +00:00
{
_closePipe();
BlenderLog.report(logvisor::Fatal, "Unable to launch blender: %s", lineBuf + 9);
2015-08-31 03:36:24 +00:00
}
else if (!strncmp(lineBuf, "NOBLENDER", 9))
2015-08-31 03:36:24 +00:00
{
_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)
2017-10-22 06:10:59 +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
}
_writeStr("ACK");
2015-05-24 04:51:16 +00:00
_readStr(lineBuf, 7);
2016-04-26 00:47:11 +00:00
if (!strcmp(lineBuf, "SLERP0"))
m_hasSlerp = false;
else if (!strcmp(lineBuf, "SLERP1"))
m_hasSlerp = true;
else
{
_closePipe();
BlenderLog.report(logvisor::Fatal, "read '%s' from blender; expected 'SLERP(0|1)'", lineBuf);
}
2015-08-31 03:36:24 +00:00
break;
}
2017-12-06 03:22:31 +00:00
#else
BlenderLog.report(logvisor::Fatal, "BlenderConnection not available on UWP");
#endif
2015-05-24 04:51:16 +00:00
}
2017-12-29 07:56:31 +00:00
Connection::~Connection()
2015-05-24 04:51:16 +00:00
{
2015-05-24 22:19:28 +00:00
_closePipe();
}
2017-12-29 07:56:31 +00:00
void Vector2f::read(Connection& conn) {conn._readBuf(&val, 8);}
void Vector3f::read(Connection& conn) {conn._readBuf(&val, 12);}
void Vector4f::read(Connection& conn) {conn._readBuf(&val, 16);}
void Matrix4f::read(Connection& conn) {conn._readBuf(&val, 64);}
void Index::read(Connection& conn) {conn._readBuf(&val, 4);}
std::streambuf::int_type
2017-12-29 07:56:31 +00:00
PyOutStream::StreamBuf::overflow(int_type ch)
{
if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
BlenderLog.report(logvisor::Fatal, "lock not held for PyOutStream writing");
if (ch != traits_type::eof() && ch != '\n' && ch != '\0')
{
m_lineBuf += char_type(ch);
return ch;
}
//printf("FLUSHING %s\n", m_lineBuf.c_str());
m_parent.m_parent->_writeStr(m_lineBuf.c_str());
char readBuf[16];
m_parent.m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "OK"))
{
if (m_deleteOnError)
m_parent.m_parent->deleteBlend();
2016-08-02 22:12:49 +00:00
m_parent.m_parent->_blenderDied();
}
m_lineBuf.clear();
return ch;
}
2015-10-04 04:35:18 +00:00
static const char* BlendTypeStrs[] =
2015-10-01 00:40:06 +00:00
{
"NONE",
"MESH",
2017-10-17 05:51:13 +00:00
"CMESH",
2015-10-01 00:40:06 +00:00
"ACTOR",
"AREA",
2015-10-24 01:21:58 +00:00
"WORLD",
"MAPAREA",
"MAPUNIVERSE",
2016-01-22 12:17:18 +00:00
"FRAME",
2018-02-24 06:15:12 +00:00
"PATH",
2015-10-04 04:35:18 +00:00
nullptr
2015-10-01 00:40:06 +00:00
};
2017-12-29 07:56:31 +00:00
bool Connection::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;
}
2017-11-13 06:13:53 +00:00
_writeStr(("CREATE \""s + path.getAbsolutePathUTF8().data() + "\" " + BlendTypeStrs[int(type)] +
2017-10-22 06:10:59 +00:00
" \"" + m_startupBlend + "\"").c_str());
2015-07-28 02:25:33 +00:00
char lineBuf[256];
_readStr(lineBuf, sizeof(lineBuf));
2015-07-28 02:25:33 +00:00
if (!strcmp(lineBuf, "FINISHED"))
{
/* Delete immediately in case save doesn't occur */
2017-11-13 06:13:53 +00:00
hecl::Unlink(path.getAbsolutePath().data());
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;
}
2017-12-29 07:56:31 +00:00
bool Connection::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;
2017-11-13 06:13:53 +00:00
_writeStr(("OPEN \""s + path.getAbsolutePathUTF8().data() + "\"").c_str());
2015-05-24 22:19:28 +00:00
char lineBuf[256];
_readStr(lineBuf, sizeof(lineBuf));
2015-05-24 22:19:28 +00:00
if (!strcmp(lineBuf, "FINISHED"))
{
2015-08-31 03:36:24 +00:00
m_loadedBlend = path;
_writeStr("GETTYPE");
_readStr(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;
}
2016-04-05 01:49:42 +00:00
m_loadedRigged = false;
if (m_loadedType == BlendType::Mesh)
{
_writeStr("GETMESHRIGGED");
_readStr(lineBuf, sizeof(lineBuf));
2016-04-05 01:49:42 +00:00
if (!strcmp("TRUE", lineBuf))
m_loadedRigged = true;
}
2015-05-24 22:19:28 +00:00
return true;
}
return false;
}
2017-12-29 07:56:31 +00:00
bool Connection::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;
}
_writeStr("SAVE");
char lineBuf[256];
_readStr(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "FINISHED"))
return true;
return false;
}
2017-12-29 07:56:31 +00:00
void Connection::deleteBlend()
2015-08-05 22:59:59 +00:00
{
2015-10-07 01:16:54 +00:00
if (m_loadedBlend)
2015-08-05 22:59:59 +00:00
{
2017-11-13 06:13:53 +00:00
hecl::Unlink(m_loadedBlend.getAbsolutePath().data());
BlenderLog.report(logvisor::Info, _S("Deleted '%s'"), m_loadedBlend.getAbsolutePath().data());
2015-10-07 01:16:54 +00:00
m_loadedBlend = ProjectPath();
2015-08-05 22:59:59 +00:00
}
}
2017-12-29 07:56:31 +00:00
PyOutStream::PyOutStream(Connection* parent, bool deleteOnError)
: std::ostream(&m_sbuf),
m_parent(parent),
m_deleteOnError(deleteOnError),
m_sbuf(*this, deleteOnError)
{
m_parent->m_pyStreamActive = true;
m_parent->_writeStr("PYBEGIN");
char readBuf[16];
m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "READY"))
BlenderLog.report(logvisor::Fatal, "unable to open PyOutStream with blender");
}
void PyOutStream::close()
{
if (m_parent && m_parent->m_lock)
{
m_parent->_writeStr("PYEND");
char readBuf[16];
m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "DONE"))
BlenderLog.report(logvisor::Fatal, "unable to close PyOutStream with blender");
m_parent->m_pyStreamActive = false;
m_parent->m_lock = false;
}
}
#if __GNUC__
__attribute__((__format__ (__printf__, 2, 3)))
#endif
void PyOutStream::format(const char* fmt, ...)
{
if (!m_parent || !m_parent->m_lock)
BlenderLog.report(logvisor::Fatal, "lock not held for PyOutStream::format()");
va_list ap;
va_start(ap, fmt);
char* result = nullptr;
#ifdef _WIN32
int length = _vscprintf(fmt, ap);
result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap);
#else
int length = vasprintf(&result, fmt, ap);
#endif
va_end(ap);
if (length > 0)
this->write(result, length);
free(result);
}
void 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);
}
2017-12-29 07:56:31 +00:00
void PyOutStream::linkBackground(const char* target, const char* sceneName)
2015-10-24 01:21:58 +00:00
{
2018-02-24 06:15:12 +00:00
if (!sceneName)
{
format("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"
" obj_scene = scene\n"
" break\n"
"if not obj_scene:\n"
" raise RuntimeError('''unable to find %s. try deleting it and restart the extract.''')\n"
"\n"
"bpy.context.scene.background_set = obj_scene\n",
target, target);
}
else
{
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);
}
}
2017-12-29 07:56:31 +00:00
void PyOutStream::AABBToBMesh(const atVec3f& min, const atVec3f& max)
{
format("bm = bmesh.new()\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.new((%f,%f,%f))\n"
"bm.verts.ensure_lookup_table()\n"
"bm.edges.new((bm.verts[0], bm.verts[1]))\n"
"bm.edges.new((bm.verts[0], bm.verts[2]))\n"
"bm.edges.new((bm.verts[0], bm.verts[4]))\n"
"bm.edges.new((bm.verts[3], bm.verts[1]))\n"
"bm.edges.new((bm.verts[3], bm.verts[2]))\n"
"bm.edges.new((bm.verts[3], bm.verts[7]))\n"
"bm.edges.new((bm.verts[5], bm.verts[1]))\n"
"bm.edges.new((bm.verts[5], bm.verts[4]))\n"
"bm.edges.new((bm.verts[5], bm.verts[7]))\n"
"bm.edges.new((bm.verts[6], bm.verts[2]))\n"
"bm.edges.new((bm.verts[6], bm.verts[4]))\n"
"bm.edges.new((bm.verts[6], bm.verts[7]))\n",
min.vec[0], min.vec[1], min.vec[2],
max.vec[0], min.vec[1], min.vec[2],
min.vec[0], max.vec[1], min.vec[2],
max.vec[0], max.vec[1], min.vec[2],
min.vec[0], min.vec[1], max.vec[2],
max.vec[0], min.vec[1], max.vec[2],
min.vec[0], max.vec[1], max.vec[2],
max.vec[0], max.vec[1], max.vec[2]);
}
void PyOutStream::centerView()
{
*this << "for obj in bpy.context.scene.objects:\n"
" if obj.type == 'CAMERA' or obj.type == 'LAMP':\n"
" obj.hide = True\n"
"\n"
"bpy.context.user_preferences.view.smooth_view = 0\n"
"for window in bpy.context.window_manager.windows:\n"
" screen = window.screen\n"
" for area in screen.areas:\n"
" if area.type == 'VIEW_3D':\n"
" for region in area.regions:\n"
" if region.type == 'WINDOW':\n"
" override = {'scene': bpy.context.scene, 'window': window, 'screen': screen, 'area': area, 'region': region}\n"
" bpy.ops.view3d.view_all(override)\n"
" break\n"
"\n"
"for obj in bpy.context.scene.objects:\n"
" if obj.type == 'CAMERA' or obj.type == 'LAMP':\n"
" obj.hide = False\n";
}
ANIMOutStream::ANIMOutStream(Connection* parent)
: m_parent(parent)
{
m_parent->_writeStr("PYANIM");
char readBuf[16];
m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "ANIMREADY"))
BlenderLog.report(logvisor::Fatal, "unable to open ANIMOutStream");
}
ANIMOutStream::~ANIMOutStream()
{
char tp = -1;
m_parent->_writeBuf(&tp, 1);
char readBuf[16];
m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "ANIMDONE"))
BlenderLog.report(logvisor::Fatal, "unable to close ANIMOutStream");
}
void ANIMOutStream::changeCurve(CurveType type, unsigned crvIdx, unsigned keyCount)
{
if (m_curCount != m_totalCount)
BlenderLog.report(logvisor::Fatal, "incomplete ANIMOutStream for change");
m_curCount = 0;
m_totalCount = keyCount;
char tp = char(type);
m_parent->_writeBuf(&tp, 1);
struct
{
uint32_t ci;
uint32_t kc;
} info = {uint32_t(crvIdx), uint32_t(keyCount)};
m_parent->_writeBuf(reinterpret_cast<const char*>(&info), 8);
m_inCurve = true;
}
void ANIMOutStream::write(unsigned frame, float val)
{
if (!m_inCurve)
BlenderLog.report(logvisor::Fatal, "changeCurve not called before write");
if (m_curCount < m_totalCount)
{
struct
{
uint32_t frm;
float val;
} key = {uint32_t(frame), val};
m_parent->_writeBuf(reinterpret_cast<const char*>(&key), 8);
++m_curCount;
}
else
BlenderLog.report(logvisor::Fatal, "ANIMOutStream keyCount overflow");
}
Mesh::SkinBind::SkinBind(Connection& conn) {conn._readBuf(&boneIdx, 8);}
void Mesh::normalizeSkinBinds()
2016-09-10 05:38:18 +00:00
{
for (std::vector<SkinBind>& skin : skins)
{
float accum = 0.f;
for (const SkinBind& bind : skin)
accum += bind.weight;
if (accum > FLT_EPSILON)
{
for (SkinBind& bind : skin)
bind.weight /= accum;
}
}
}
2017-12-29 07:56:31 +00:00
Mesh::Mesh(Connection& conn, HMDLTopology topologyIn, int skinSlotCount, SurfProgFunc& surfProg)
: topology(topologyIn), sceneXf(conn), 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._readStr(name, 128);
2015-10-02 04:06:45 +00:00
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);
}
normalizeSkinBinds();
2015-10-02 04:06:45 +00:00
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
}
/* Custom properties */
uint32_t propCount;
conn._readBuf(&propCount, 4);
std::string keyBuf;
std::string valBuf;
for (uint32_t i=0 ; i<propCount ; ++i)
{
uint32_t kLen;
conn._readBuf(&kLen, 4);
keyBuf.assign(kLen, '\0');
conn._readBuf(&keyBuf[0], kLen);
uint32_t vLen;
conn._readBuf(&vLen, 4);
valBuf.assign(vLen, '\0');
conn._readBuf(&valBuf[0], vLen);
customProps[keyBuf] = valBuf;
}
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
}
2017-12-29 07:56:31 +00:00
Mesh Mesh::getContiguousSkinningVersion() const
2015-10-22 02:01:08 +00:00
{
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;
}
2017-12-29 07:56:31 +00:00
Material::Material(Connection& conn)
2015-10-03 01:53:45 +00:00
{
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);
2017-11-13 06:13:53 +00:00
SystemStringConv absolute(readStr);
2015-10-14 23:06:47 +00:00
2015-10-07 01:16:54 +00:00
SystemString relative =
2017-12-29 07:56:31 +00:00
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
texs.emplace_back(conn.getBlendPath().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;
}
2017-02-24 08:27:07 +00:00
conn._readBuf(&transparent, 1);
2015-10-03 01:53:45 +00:00
}
2017-12-29 07:56:31 +00:00
Mesh::Surface::Surface(Connection& 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
}
2017-12-29 07:56:31 +00:00
Mesh::Surface::Vert::Vert(Connection& 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
}
2017-12-29 07:56:31 +00:00
bool Mesh::Surface::Vert::operator==(const Vert& other) const
{
if (iPos != other.iPos)
return false;
if (iNorm != other.iNorm)
return false;
for (int i=0 ; i<4 ; ++i)
if (iColor[i] != other.iColor[i])
return false;
for (int i=0 ; i<8 ; ++i)
if (iUv[i] != other.iUv[i])
return false;
if (iSkin != other.iSkin)
return false;
return true;
}
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;
}
2017-12-29 07:56:31 +00:00
void Mesh::SkinBanks::Bank::addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs)
{
for (uint32_t sidx : skinIdxs)
{
m_skinIdxs.push_back(sidx);
for (const SkinBind& bind : parent.skins[sidx])
{
bool found = false;
for (uint32_t bidx : m_boneIdxs)
{
if (bidx == bind.boneIdx)
{
found = true;
break;
}
}
if (!found)
m_boneIdxs.push_back(bind.boneIdx);
}
}
}
size_t Mesh::SkinBanks::Bank::lookupLocalBoneIdx(uint32_t boneIdx) const
{
for (size_t i=0 ; i<m_boneIdxs.size() ; ++i)
if (m_boneIdxs[i] == boneIdx)
return i;
return -1;
}
std::vector<Mesh::SkinBanks::Bank>::iterator Mesh::SkinBanks::addSkinBank(int skinSlotCount)
{
banks.emplace_back();
if (skinSlotCount > 0)
banks.back().m_skinIdxs.reserve(skinSlotCount);
return banks.end() - 1;
}
uint32_t 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
}
2017-12-29 07:56:31 +00:00
ColMesh::ColMesh(Connection& conn)
{
uint32_t matCount;
conn._readBuf(&matCount, 4);
materials.reserve(matCount);
for (uint32_t i=0 ; i<matCount ; ++i)
materials.emplace_back(conn);
uint32_t count;
conn._readBuf(&count, 4);
verts.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
verts.emplace_back(conn);
conn._readBuf(&count, 4);
edges.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
edges.emplace_back(conn);
conn._readBuf(&count, 4);
trianges.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
trianges.emplace_back(conn);
}
2017-12-29 07:56:31 +00:00
ColMesh::Material::Material(Connection& conn)
{
uint32_t nameLen;
conn._readBuf(&nameLen, 4);
if (nameLen)
{
name.assign(nameLen, '\0');
conn._readBuf(&name[0], nameLen);
}
2016-12-28 19:41:03 +00:00
conn._readBuf(&unknown, 42);
}
2017-12-29 07:56:31 +00:00
ColMesh::Edge::Edge(Connection& conn)
{
conn._readBuf(this, 9);
}
2017-12-29 07:56:31 +00:00
ColMesh::Triangle::Triangle(Connection& conn)
{
2017-12-09 05:15:26 +00:00
conn._readBuf(this, 17);
}
2017-12-29 07:56:31 +00:00
World::Area::Dock::Dock(Connection& conn)
2016-10-01 23:18:52 +00:00
{
verts[0].read(conn);
verts[1].read(conn);
verts[2].read(conn);
verts[3].read(conn);
targetArea.read(conn);
targetDock.read(conn);
}
2017-12-29 07:56:31 +00:00
World::Area::Area(Connection& conn)
2016-10-01 23:18:52 +00:00
{
std::string name;
uint32_t nameLen;
conn._readBuf(&nameLen, 4);
if (nameLen)
{
name.assign(nameLen, '\0');
conn._readBuf(&name[0], nameLen);
}
2017-12-29 07:56:31 +00:00
path.assign(conn.getBlendPath().getParentPath(), name);
2016-10-01 23:18:52 +00:00
aabb[0].read(conn);
aabb[1].read(conn);
transform.read(conn);
uint32_t dockCount;
conn._readBuf(&dockCount, 4);
docks.reserve(dockCount);
for (uint32_t i=0 ; i<dockCount ; ++i)
docks.emplace_back(conn);
}
2017-12-29 07:56:31 +00:00
World::World(Connection& conn)
2016-09-30 22:41:01 +00:00
{
2016-10-01 23:18:52 +00:00
uint32_t areaCount;
conn._readBuf(&areaCount, 4);
areas.reserve(areaCount);
for (uint32_t i=0 ; i<areaCount ; ++i)
areas.emplace_back(conn);
2016-09-30 22:41:01 +00:00
}
2017-12-29 07:56:31 +00:00
Light::Light(Connection& conn)
2016-08-11 19:51:41 +00:00
: sceneXf(conn), color(conn)
{
conn._readBuf(&layer, 29);
2017-02-24 08:27:07 +00:00
uint32_t nameLen;
conn._readBuf(&nameLen, 4);
if (nameLen)
{
name.assign(nameLen, '\0');
conn._readBuf(&name[0], nameLen);
}
2016-08-11 19:51:41 +00:00
}
2017-12-29 07:56:31 +00:00
MapArea::Surface::Surface(Connection& conn)
{
centerOfMass.read(conn);
normal.read(conn);
2017-03-20 21:14:20 +00:00
conn._readBuf(&start, 8);
uint32_t borderCount;
conn._readBuf(&borderCount, 4);
borders.reserve(borderCount);
for (int i=0 ; i<borderCount ; ++i)
{
borders.emplace_back();
std::pair<Index, Index>& idx = borders.back();
conn._readBuf(&idx, 8);
}
}
2017-12-29 07:56:31 +00:00
MapArea::POI::POI(Connection& conn)
{
conn._readBuf(&type, 12);
xf.read(conn);
}
2017-12-29 07:56:31 +00:00
MapArea::MapArea(Connection& conn)
{
2017-04-18 23:38:23 +00:00
visType.read(conn);
uint32_t vertCount;
conn._readBuf(&vertCount, 4);
verts.reserve(vertCount);
for (int i=0 ; i<vertCount ; ++i)
verts.emplace_back(conn);
uint8_t isIdx;
conn._readBuf(&isIdx, 1);
while (isIdx)
{
indices.emplace_back(conn);
conn._readBuf(&isIdx, 1);
}
uint32_t surfCount;
conn._readBuf(&surfCount, 4);
surfaces.reserve(surfCount);
for (int i=0 ; i<surfCount ; ++i)
surfaces.emplace_back(conn);
uint32_t poiCount;
conn._readBuf(&poiCount, 4);
pois.reserve(poiCount);
for (int i=0 ; i<poiCount ; ++i)
pois.emplace_back(conn);
}
2017-12-29 07:56:31 +00:00
MapUniverse::World::World(Connection& conn)
{
uint32_t nameLen;
conn._readBuf(&nameLen, 4);
if (nameLen)
{
name.assign(nameLen, '\0');
conn._readBuf(&name[0], nameLen);
}
xf.read(conn);
uint32_t hexCount;
conn._readBuf(&hexCount, 4);
hexagons.reserve(hexCount);
for (int i=0 ; i<hexCount ; ++i)
hexagons.emplace_back(conn);
color.read(conn);
uint32_t pathLen;
conn._readBuf(&pathLen, 4);
if (pathLen)
{
std::string path;
path.assign(pathLen, '\0');
conn._readBuf(&path[0], pathLen);
2017-11-13 06:13:53 +00:00
hecl::SystemStringConv sysPath(path);
2017-12-29 07:56:31 +00:00
worldPath.assign(conn.getBlendPath().getProject().getProjectWorkingPath(), sysPath.sys_str());
}
}
2017-12-29 07:56:31 +00:00
MapUniverse::MapUniverse(Connection& conn)
{
uint32_t pathLen;
conn._readBuf(&pathLen, 4);
if (pathLen)
{
std::string path;
path.assign(pathLen, '\0');
conn._readBuf(&path[0], pathLen);
2017-11-13 06:13:53 +00:00
hecl::SystemStringConv sysPath(path);
SystemString pathRel =
2017-12-29 07:56:31 +00:00
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(sysPath.sys_str());
hexagonPath.assign(conn.getBlendPath().getProject().getProjectWorkingPath(), pathRel);
}
uint32_t worldCount;
conn._readBuf(&worldCount, 4);
worlds.reserve(worldCount);
for (int i=0 ; i<worldCount ; ++i)
worlds.emplace_back(conn);
}
2017-12-29 07:56:31 +00:00
Actor::Actor(Connection& conn)
2015-10-23 00:44:37 +00:00
{
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);
}
2018-02-24 06:15:12 +00:00
PathMesh::PathMesh(Connection& conn)
{
2018-02-25 08:23:00 +00:00
uint32_t dataSize;
conn._readBuf(&dataSize, 4);
data.resize(dataSize);
conn._readBuf(data.data(), dataSize);
2018-02-24 06:15:12 +00:00
}
2017-12-29 07:56:31 +00:00
const Bone* Armature::lookupBone(const char* name) const
{
for (const Bone& b : bones)
if (!b.name.compare(name))
return &b;
return nullptr;
}
const Bone* Armature::getParent(const Bone* bone) const
{
if (bone->parent < 0)
return nullptr;
return &bones[bone->parent];
}
const Bone* Armature::getChild(const Bone* bone, size_t child) const
{
if (child >= bone->children.size())
return nullptr;
int32_t cIdx = bone->children[child];
if (cIdx < 0)
return nullptr;
return &bones[cIdx];
}
const Bone* Armature::getRoot() const
{
for (const Bone& b : bones)
if (b.parent < 0)
return &b;
return nullptr;
}
Armature::Armature(Connection& conn)
2015-10-23 00:44:37 +00:00
{
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);
}
2017-12-29 07:56:31 +00:00
Bone::Bone(Connection& conn)
2015-10-23 00:44:37 +00:00
{
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);
}
}
2017-12-29 07:56:31 +00:00
Actor::Subtype::Subtype(Connection& conn)
2015-10-23 00:44:37 +00:00
{
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);
2017-11-13 06:13:53 +00:00
SystemStringConv meshPathAbs(meshPath);
2015-10-23 00:44:37 +00:00
SystemString meshPathRel =
2017-12-29 07:56:31 +00:00
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(meshPathAbs.sys_str());
mesh.assign(conn.getBlendPath().getProject().getProjectWorkingPath(), meshPathRel);
2015-10-23 00:44:37 +00:00
}
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);
2017-11-13 06:13:53 +00:00
SystemStringConv meshPathAbs(meshPath);
2015-10-23 00:44:37 +00:00
SystemString meshPathRel =
2017-12-29 07:56:31 +00:00
conn.getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(meshPathAbs.sys_str());
2015-10-23 00:44:37 +00:00
overlayMeshes.emplace_back(std::move(overlayName),
2017-12-29 07:56:31 +00:00
ProjectPath(conn.getBlendPath().getProject().getProjectWorkingPath(), meshPathRel));
2015-10-23 00:44:37 +00:00
}
}
}
2017-12-29 07:56:31 +00:00
Action::Action(Connection& conn)
2015-10-23 00:44:37 +00:00
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
conn._readBuf(&interval, 4);
conn._readBuf(&additive, 1);
2016-09-06 05:51:11 +00:00
conn._readBuf(&looping, 1);
2015-10-23 00:44:37 +00:00
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)
{
2018-01-10 06:16:18 +00:00
//printf("AABB %s %d\n", name.c_str(), i);
2015-10-23 00:44:37 +00:00
subtypeAABBs.emplace_back();
subtypeAABBs.back().first.read(conn);
subtypeAABBs.back().second.read(conn);
}
}
2017-12-29 07:56:31 +00:00
Action::Channel::Channel(Connection& conn)
2015-10-23 00:44:37 +00:00
{
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);
}
2017-12-29 07:56:31 +00:00
Action::Channel::Key::Key(Connection& conn, uint32_t attrMask)
2015-10-23 00:44:37 +00:00
{
if (attrMask & 1)
rotation.read(conn);
if (attrMask & 2)
position.read(conn);
if (attrMask & 4)
scale.read(conn);
}
2017-12-29 07:56:31 +00:00
DataStream::DataStream(Connection* parent)
: m_parent(parent)
{
2017-12-29 07:56:31 +00:00
m_parent->m_dataStreamActive = true;
m_parent->_writeStr("DATABEGIN");
char readBuf[16];
m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "READY"))
BlenderLog.report(logvisor::Fatal, "unable to open DataStream with blender");
}
void DataStream::close()
{
if (m_parent && m_parent->m_lock)
{
m_parent->_writeStr("DATAEND");
char readBuf[16];
m_parent->_readStr(readBuf, 16);
if (strcmp(readBuf, "DONE"))
BlenderLog.report(logvisor::Fatal, "unable to close DataStream with blender");
m_parent->m_dataStreamActive = false;
m_parent->m_lock = false;
}
}
std::vector<std::string> DataStream::getMeshList()
{
m_parent->_writeStr("MESHLIST");
uint32_t count;
m_parent->_readBuf(&count, 4);
std::vector<std::string> retval;
retval.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
{
char name[128];
m_parent->_readStr(name, 128);
retval.push_back(name);
}
return retval;
}
std::vector<std::string> DataStream::getLightList()
{
m_parent->_writeStr("LIGHTLIST");
uint32_t count;
m_parent->_readBuf(&count, 4);
std::vector<std::string> retval;
retval.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
{
char name[128];
m_parent->_readStr(name, 128);
retval.push_back(name);
}
return retval;
}
std::pair<atVec3f, atVec3f> DataStream::getMeshAABB()
{
if (m_parent->m_loadedType != BlendType::Mesh &&
m_parent->m_loadedType != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not a MESH or ACTOR blend"),
2017-11-13 06:13:53 +00:00
m_parent->m_loadedBlend.getAbsolutePath().data());
2017-12-29 07:56:31 +00:00
m_parent->_writeStr("MESHAABB");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable get AABB: %s", readBuf);
Vector3f minPt(*m_parent);
Vector3f maxPt(*m_parent);
return std::make_pair(minPt.val, maxPt.val);
}
const char* DataStream::MeshOutputModeString(HMDLTopology topology)
{
static const char* STRS[] = {"TRIANGLES", "TRISTRIPS"};
return STRS[int(topology)];
}
Mesh DataStream::compileMesh(HMDLTopology topology, int skinSlotCount, Mesh::SurfProgFunc surfProg)
{
if (m_parent->getBlendType() != BlendType::Mesh)
BlenderLog.report(logvisor::Fatal, _S("%s is not a MESH blend"),
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
snprintf(req, 128, "MESHCOMPILE %s %d",
MeshOutputModeString(topology), skinSlotCount);
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook mesh: %s", readBuf);
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
}
2017-12-29 07:56:31 +00:00
Mesh DataStream::compileMesh(std::string_view name, HMDLTopology topology,
int skinSlotCount, Mesh::SurfProgFunc surfProg)
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
2017-11-13 06:13:53 +00:00
snprintf(req, 128, "MESHCOMPILENAME %s %s %d", name.data(),
MeshOutputModeString(topology), skinSlotCount);
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
2017-11-13 06:13:53 +00:00
BlenderLog.report(logvisor::Fatal, "unable to cook mesh '%s': %s", name.data(), readBuf);
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
}
2017-12-29 07:56:31 +00:00
ColMesh DataStream::compileColMesh(std::string_view name)
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
2017-11-13 06:13:53 +00:00
snprintf(req, 128, "MESHCOMPILENAMECOLLISION %s", name.data());
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
2017-11-13 06:13:53 +00:00
BlenderLog.report(logvisor::Fatal, "unable to cook collision mesh '%s': %s", name.data(), readBuf);
return ColMesh(*m_parent);
}
2017-12-29 07:56:31 +00:00
std::vector<ColMesh> DataStream::compileColMeshes()
2017-10-17 05:51:13 +00:00
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::ColMesh)
2017-10-17 05:51:13 +00:00
BlenderLog.report(logvisor::Fatal, _S("%s is not a CMESH blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
2017-10-17 05:51:13 +00:00
char req[128];
snprintf(req, 128, "MESHCOMPILECOLLISIONALL");
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook collision meshes: %s", readBuf);
uint32_t meshCount;
m_parent->_readBuf(&meshCount, 4);
2017-12-29 07:56:31 +00:00
std::vector<ColMesh> ret;
2017-10-17 05:51:13 +00:00
ret.reserve(meshCount);
for (uint32_t i=0 ; i<meshCount ; ++i)
ret.emplace_back(*m_parent);
return ret;
}
2017-12-29 07:56:31 +00:00
Mesh DataStream::compileAllMeshes(HMDLTopology topology, int skinSlotCount,
float maxOctantLength, Mesh::SurfProgFunc surfProg)
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
snprintf(req, 128, "MESHCOMPILEALL %s %d %f",
MeshOutputModeString(topology),
skinSlotCount, maxOctantLength);
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook all meshes: %s", readBuf);
return Mesh(*m_parent, topology, skinSlotCount, surfProg);
}
2017-12-29 07:56:31 +00:00
std::vector<Light> DataStream::compileLights()
2016-08-11 19:51:41 +00:00
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Area)
2016-08-11 19:51:41 +00:00
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
2016-08-11 19:51:41 +00:00
m_parent->_writeStr("LIGHTCOMPILEALL");
2016-08-11 19:51:41 +00:00
char readBuf[256];
m_parent->_readStr(readBuf, 256);
2016-08-11 19:51:41 +00:00
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to gather all lights: %s", readBuf);
uint32_t lightCount;
m_parent->_readBuf(&lightCount, 4);
2017-12-29 07:56:31 +00:00
std::vector<Light> ret;
2016-08-11 19:51:41 +00:00
ret.reserve(lightCount);
2016-09-11 21:16:16 +00:00
for (uint32_t i=0 ; i<lightCount ; ++i)
2016-08-11 19:51:41 +00:00
ret.emplace_back(*m_parent);
return ret;
}
2018-02-24 06:15:12 +00:00
PathMesh DataStream::compilePathMesh()
{
if (m_parent->getBlendType() != BlendType::PathMesh)
BlenderLog.report(logvisor::Fatal, _S("%s is not a PATH blend"),
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("MESHCOMPILEPATH");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to path collision mesh: %s", readBuf);
return PathMesh(*m_parent);
}
2017-12-29 07:56:31 +00:00
void DataStream::compileGuiFrame(std::string_view pathOut, int version)
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Frame)
BlenderLog.report(logvisor::Fatal, _S("%s is not a FRAME blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
char req[512];
2017-11-13 06:13:53 +00:00
snprintf(req, 512, "FRAMECOMPILE '%s' %d", pathOut.data(), version);
m_parent->_writeStr(req);
2017-01-17 07:12:49 +00:00
char readBuf[1024];
m_parent->_readStr(readBuf, 1024);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile frame: %s", readBuf);
2017-01-17 07:12:49 +00:00
while (true)
{
m_parent->_readStr(readBuf, 1024);
if (!strcmp(readBuf, "FRAMEDONE"))
break;
std::string readStr(readBuf);
2017-11-13 06:13:53 +00:00
SystemStringConv absolute(readStr);
2017-12-29 07:56:31 +00:00
auto& proj = m_parent->getBlendPath().getProject();
2017-01-17 07:12:49 +00:00
SystemString relative;
2017-02-25 07:58:36 +00:00
if (PathRelative(absolute.c_str()))
2017-01-17 07:12:49 +00:00
relative = absolute.sys_str();
else
2017-11-13 06:13:53 +00:00
relative = proj.getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
2017-01-17 07:12:49 +00:00
hecl::ProjectPath path(proj.getProjectWorkingPath(), relative);
snprintf(req, 512, "%016" PRIX64 , path.hash().val64());
2017-01-17 07:12:49 +00:00
m_parent->_writeStr(req);
}
}
2017-12-29 07:56:31 +00:00
std::vector<ProjectPath> DataStream::getTextures()
2016-10-02 22:34:10 +00:00
{
m_parent->_writeStr("GETTEXTURES");
2016-10-02 22:34:10 +00:00
char readBuf[256];
m_parent->_readStr(readBuf, 256);
2016-10-02 22:34:10 +00:00
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to get textures: %s", readBuf);
uint32_t texCount;
m_parent->_readBuf(&texCount, 4);
std::vector<ProjectPath> texs;
texs.reserve(texCount);
for (uint32_t i=0 ; i<texCount ; ++i)
{
uint32_t bufSz;
m_parent->_readBuf(&bufSz, 4);
std::string readStr(bufSz, ' ');
m_parent->_readBuf(&readStr[0], bufSz);
2017-11-13 06:13:53 +00:00
SystemStringConv absolute(readStr);
2016-10-02 22:34:10 +00:00
SystemString relative =
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getProject().getProjectRootPath().getProjectRelativeFromAbsolute(absolute.sys_str());
texs.emplace_back(m_parent->getBlendPath().getProject().getProjectWorkingPath(), relative);
2016-10-02 22:34:10 +00:00
}
return texs;
}
2017-12-29 07:56:31 +00:00
Actor DataStream::compileActor()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("ACTORCOMPILE");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile actor: %s", readBuf);
return Actor(*m_parent);
}
2017-12-29 07:56:31 +00:00
Actor DataStream::compileActorCharacterOnly()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("ACTORCOMPILECHARACTERONLY");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile actor: %s", readBuf);
return Actor(*m_parent);
}
2017-12-29 07:56:31 +00:00
Action DataStream::compileActionChannelsOnly(std::string_view name)
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
2017-11-13 06:13:53 +00:00
snprintf(req, 128, "ACTIONCOMPILECHANNELSONLY %s", name.data());
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile action: %s", readBuf);
2017-12-29 07:56:31 +00:00
return Action(*m_parent);
}
2017-12-29 07:56:31 +00:00
World DataStream::compileWorld()
2016-09-30 22:41:01 +00:00
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::World)
2016-09-30 22:41:01 +00:00
BlenderLog.report(logvisor::Fatal, _S("%s is not an WORLD blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
2016-09-30 22:41:01 +00:00
m_parent->_writeStr("WORLDCOMPILE");
2016-09-30 22:41:01 +00:00
char readBuf[256];
m_parent->_readStr(readBuf, 256);
2016-09-30 22:41:01 +00:00
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile world: %s", readBuf);
return World(*m_parent);
}
2017-12-29 07:56:31 +00:00
std::vector<std::string> DataStream::getArmatureNames()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("GETARMATURENAMES");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to get armatures of actor: %s", readBuf);
std::vector<std::string> ret;
uint32_t armCount;
m_parent->_readBuf(&armCount, 4);
ret.reserve(armCount);
for (uint32_t i=0 ; i<armCount ; ++i)
{
ret.emplace_back();
std::string& name = ret.back();
uint32_t bufSz;
m_parent->_readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
m_parent->_readBuf(&name[0], bufSz);
}
return ret;
}
2017-12-29 07:56:31 +00:00
std::vector<std::string> DataStream::getSubtypeNames()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("GETSUBTYPENAMES");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to get subtypes of actor: %s", readBuf);
std::vector<std::string> ret;
uint32_t subCount;
m_parent->_readBuf(&subCount, 4);
ret.reserve(subCount);
for (uint32_t i=0 ; i<subCount ; ++i)
{
ret.emplace_back();
std::string& name = ret.back();
uint32_t bufSz;
m_parent->_readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
m_parent->_readBuf(&name[0], bufSz);
}
return ret;
}
2017-12-29 07:56:31 +00:00
std::vector<std::string> DataStream::getActionNames()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("GETACTIONNAMES");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to get actions of actor: %s", readBuf);
std::vector<std::string> ret;
uint32_t actCount;
m_parent->_readBuf(&actCount, 4);
ret.reserve(actCount);
for (uint32_t i=0 ; i<actCount ; ++i)
{
ret.emplace_back();
std::string& name = ret.back();
uint32_t bufSz;
m_parent->_readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
m_parent->_readBuf(&name[0], bufSz);
}
2017-10-25 07:46:32 +00:00
return ret;
}
2017-12-29 07:56:31 +00:00
std::vector<std::string> DataStream::getSubtypeOverlayNames(std::string_view name)
2017-10-25 07:46:32 +00:00
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
2017-10-25 07:46:32 +00:00
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
2017-10-25 07:46:32 +00:00
char req[128];
2017-11-13 06:13:53 +00:00
snprintf(req, 128, "GETSUBTYPEOVERLAYNAMES %s", name.data());
2017-10-25 07:46:32 +00:00
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to get subtype overlays of actor: %s", readBuf);
std::vector<std::string> ret;
uint32_t subCount;
m_parent->_readBuf(&subCount, 4);
ret.reserve(subCount);
for (uint32_t i=0 ; i<subCount ; ++i)
{
ret.emplace_back();
std::string& name = ret.back();
uint32_t bufSz;
m_parent->_readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
m_parent->_readBuf(&name[0], bufSz);
}
return ret;
}
2017-12-29 07:56:31 +00:00
std::unordered_map<std::string, Matrix3f>
DataStream::getBoneMatrices(std::string_view name)
{
if (name.empty())
return {};
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Actor)
BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
char req[128];
2017-11-13 06:13:53 +00:00
snprintf(req, 128, "GETBONEMATRICES %s", name.data());
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to get matrices of armature: %s", readBuf);
std::unordered_map<std::string, Matrix3f> ret;
uint32_t boneCount;
m_parent->_readBuf(&boneCount, 4);
ret.reserve(boneCount);
for (uint32_t i=0 ; i<boneCount ; ++i)
{
std::string name;
uint32_t bufSz;
m_parent->_readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
m_parent->_readBuf(&name[0], bufSz);
Matrix3f matOut;
for (int i=0 ; i<3 ; ++i)
{
for (int j=0 ; j<3 ; ++j)
{
float val;
m_parent->_readBuf(&val, 4);
matOut[i].vec[j] = val;
}
reinterpret_cast<atVec4f&>(matOut[i]).vec[3] = 0.f;
}
ret.emplace(std::make_pair(std::move(name), std::move(matOut)));
}
return ret;
}
2017-12-29 07:56:31 +00:00
bool DataStream::renderPvs(std::string_view path, const atVec3f& location)
2017-02-24 08:27:07 +00:00
{
if (path.empty())
return false;
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Area)
2017-02-24 08:27:07 +00:00
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
2017-02-24 08:27:07 +00:00
char req[256];
2017-11-13 06:13:53 +00:00
snprintf(req, 256, "RENDERPVS %s %f %f %f", path.data(),
2017-02-24 08:27:07 +00:00
location.vec[0], location.vec[1], location.vec[2]);
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to render PVS for: %s; %s",
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePathUTF8().data(), readBuf);
2017-02-24 08:27:07 +00:00
return true;
}
2017-12-29 07:56:31 +00:00
bool DataStream::renderPvsLight(std::string_view path, std::string_view lightName)
2017-02-24 08:27:07 +00:00
{
if (path.empty())
return false;
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::Area)
2017-02-24 08:27:07 +00:00
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
2017-02-24 08:27:07 +00:00
char req[256];
2017-11-13 06:13:53 +00:00
snprintf(req, 256, "RENDERPVSLIGHT %s %s", path.data(), lightName.data());
2017-02-24 08:27:07 +00:00
m_parent->_writeStr(req);
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
2017-11-13 06:13:53 +00:00
BlenderLog.report(logvisor::Fatal, "unable to render PVS light %s for: %s; %s", lightName.data(),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePathUTF8().data(), readBuf);
2017-02-24 08:27:07 +00:00
return true;
}
2017-12-29 07:56:31 +00:00
MapArea DataStream::compileMapArea()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::MapArea)
BlenderLog.report(logvisor::Fatal, _S("%s is not a MAPAREA blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("MAPAREACOMPILE");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile map area: %s; %s",
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePathUTF8().data(), readBuf);
return {*m_parent};
}
2017-12-29 07:56:31 +00:00
MapUniverse DataStream::compileMapUniverse()
{
2017-12-29 07:56:31 +00:00
if (m_parent->getBlendType() != BlendType::MapUniverse)
BlenderLog.report(logvisor::Fatal, _S("%s is not a MAPUNIVERSE blend"),
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePath().data());
m_parent->_writeStr("MAPUNIVERSECOMPILE");
char readBuf[256];
m_parent->_readStr(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to compile map universe: %s; %s",
2017-12-29 07:56:31 +00:00
m_parent->getBlendPath().getAbsolutePathUTF8().data(), readBuf);
return {*m_parent};
}
2017-12-29 07:56:31 +00:00
void Connection::quitBlender()
2015-05-24 04:51:16 +00:00
{
char lineBuf[256];
if (m_lock)
{
if (m_pyStreamActive)
{
_writeStr("PYEND");
_readStr(lineBuf, sizeof(lineBuf));
m_pyStreamActive = false;
}
else if (m_dataStreamActive)
{
_writeStr("DATAEND");
_readStr(lineBuf, sizeof(lineBuf));
m_dataStreamActive = false;
}
m_lock = false;
}
_writeStr("QUIT");
_readStr(lineBuf, sizeof(lineBuf));
2015-05-24 04:51:16 +00:00
}
2015-07-28 02:25:33 +00:00
2017-12-29 07:56:31 +00:00
Connection& Connection::SharedConnection()
2016-03-28 22:39:18 +00:00
{
return SharedBlenderToken.getBlenderConnection();
}
2017-12-29 07:56:31 +00:00
void Connection::Shutdown()
2016-03-28 22:39:18 +00:00
{
SharedBlenderToken.shutdown();
}
2017-12-29 07:56:31 +00:00
Connection& Token::getBlenderConnection()
{
if (!m_conn)
m_conn = std::make_unique<Connection>(hecl::VerbosityLevel);
return *m_conn;
}
void Token::shutdown()
{
if (m_conn)
{
m_conn->quitBlender();
m_conn.reset();
BlenderLog.report(logvisor::Info, "BlenderConnection Shutdown Successful");
}
}
Token::~Token() { shutdown(); }
HMDLBuffers::HMDLBuffers(HMDLMeta&& meta,
size_t vboSz, const std::vector<atUint32>& iboData,
std::vector<Surface>&& surfaces,
const Mesh::SkinBanks& skinBanks)
: m_meta(std::move(meta)),
m_vboSz(vboSz), m_vboData(new uint8_t[vboSz]),
m_iboSz(iboData.size()*4), m_iboData(new uint8_t[iboData.size()*4]),
m_surfaces(std::move(surfaces)), m_skinBanks(skinBanks)
{
if (m_iboSz)
{
athena::io::MemoryWriter w(m_iboData.get(), m_iboSz);
w.enumerateLittle(iboData);
}
}
2015-07-28 02:25:33 +00:00
}