metaforce/hecl/blender/BlenderConnection.hpp

470 lines
15 KiB
C++
Raw Normal View History

2015-07-22 19:14:50 +00:00
#ifndef BLENDERCONNECTION_HPP
#define BLENDERCONNECTION_HPP
2015-05-24 04:51:16 +00:00
#if _WIN32
2015-08-31 03:36:24 +00:00
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
2015-09-27 04:35:36 +00:00
#ifndef NOMINMAX
#define NOMINMAX
#endif
2015-05-24 04:51:16 +00:00
#include <windows.h>
#else
#include <unistd.h>
2015-05-24 04:51:16 +00:00
#endif
2015-07-22 19:14:50 +00:00
#include <stdint.h>
2015-08-03 02:05:04 +00:00
#include <stdio.h>
2015-05-24 22:19:28 +00:00
#include <string>
#include <functional>
2015-10-12 05:19:04 +00:00
#include <iostream>
2015-10-17 02:06:27 +00:00
#include <unordered_map>
2015-05-24 22:19:28 +00:00
2015-07-28 02:25:33 +00:00
#include "HECL/HECL.hpp"
2015-10-09 02:08:10 +00:00
#include <Athena/Types.hpp>
2015-07-28 02:25:33 +00:00
namespace HECL
{
extern LogVisor::LogModule BlenderLog;
extern class BlenderConnection* SharedBlenderConnection;
2015-07-22 19:14:50 +00:00
class BlenderConnection
{
2015-10-04 05:08:24 +00:00
public:
enum BlendType
{
TypeNone,
TypeMesh,
TypeActor,
TypeArea
};
private:
2015-08-31 03:36:24 +00:00
bool m_lock = false;
2015-05-24 04:51:16 +00:00
#if _WIN32
HANDLE m_blenderProc;
#else
pid_t m_blenderProc;
2015-08-31 03:36:24 +00:00
#endif
2015-05-24 04:51:16 +00:00
int m_readpipe[2];
int m_writepipe[2];
2015-10-04 05:08:24 +00:00
BlendType m_loadedType = TypeNone;
2015-10-07 01:16:54 +00:00
ProjectPath m_loadedBlend;
2015-09-06 20:08:23 +00:00
std::string m_startupBlend;
2015-05-24 22:19:28 +00:00
size_t _readLine(char* buf, size_t bufSz);
size_t _writeLine(const char* buf);
2015-10-02 04:06:45 +00:00
size_t _readBuf(void* buf, size_t len);
size_t _writeBuf(const void* buf, size_t len);
2015-05-24 22:19:28 +00:00
void _closePipe();
public:
2015-09-22 01:41:38 +00:00
BlenderConnection(int verbosityLevel=1);
2015-07-22 19:14:50 +00:00
~BlenderConnection();
2015-05-24 04:51:16 +00:00
2015-10-07 01:16:54 +00:00
bool createBlend(const ProjectPath& path, BlendType type);
2015-10-04 05:08:24 +00:00
BlendType getBlendType() const {return m_loadedType;}
2015-10-16 08:02:59 +00:00
bool openBlend(const ProjectPath& path, bool force=false);
bool saveBlend();
2015-08-05 22:59:59 +00:00
void deleteBlend();
2015-07-25 23:01:02 +00:00
class PyOutStream : public std::ostream
{
friend class BlenderConnection;
BlenderConnection* m_parent;
2015-08-05 22:59:59 +00:00
bool m_deleteOnError;
2015-07-25 23:01:02 +00:00
struct StreamBuf : std::streambuf
{
2015-08-05 22:59:59 +00:00
PyOutStream& m_parent;
2015-07-28 02:25:33 +00:00
std::string m_lineBuf;
2015-08-05 22:59:59 +00:00
bool m_deleteOnError;
StreamBuf(PyOutStream& parent, bool deleteOnError)
: m_parent(parent), m_deleteOnError(deleteOnError) {}
2015-07-25 23:01:02 +00:00
StreamBuf(const StreamBuf& other) = delete;
StreamBuf(StreamBuf&& other) = default;
int_type overflow(int_type ch)
{
2015-08-16 23:01:35 +00:00
if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
2015-08-05 22:59:59 +00:00
BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream writing");
2015-08-31 03:36:24 +00:00
if (ch != traits_type::eof() && ch != '\n' && ch != '\0')
2015-07-25 23:01:02 +00:00
{
2015-07-28 02:25:33 +00:00
m_lineBuf += char_type(ch);
return ch;
2015-07-25 23:01:02 +00:00
}
2015-08-31 03:36:24 +00:00
//printf("FLUSHING %s\n", m_lineBuf.c_str());
2015-08-05 22:59:59 +00:00
m_parent.m_parent->_writeLine(m_lineBuf.c_str());
2015-07-28 02:25:33 +00:00
char readBuf[16];
2015-08-05 22:59:59 +00:00
m_parent.m_parent->_readLine(readBuf, 16);
2015-07-28 02:25:33 +00:00
if (strcmp(readBuf, "OK"))
2015-08-05 22:59:59 +00:00
{
if (m_deleteOnError)
m_parent.m_parent->deleteBlend();
2015-07-28 02:25:33 +00:00
BlenderLog.report(LogVisor::FatalError, "error sending '%s' to blender", m_lineBuf.c_str());
2015-08-05 22:59:59 +00:00
}
2015-07-28 02:25:33 +00:00
m_lineBuf.clear();
2015-07-25 23:01:02 +00:00
return ch;
}
} m_sbuf;
2015-08-05 22:59:59 +00:00
PyOutStream(BlenderConnection* parent, bool deleteOnError)
2015-09-03 01:43:20 +00:00
: std::ostream(&m_sbuf),
m_parent(parent),
2015-08-05 22:59:59 +00:00
m_deleteOnError(deleteOnError),
2015-09-03 01:43:20 +00:00
m_sbuf(*this, deleteOnError)
2015-07-25 23:01:02 +00:00
{
2015-08-16 23:01:35 +00:00
m_parent->m_lock = true;
2015-07-28 02:25:33 +00:00
m_parent->_writeLine("PYBEGIN");
char readBuf[16];
m_parent->_readLine(readBuf, 16);
if (strcmp(readBuf, "READY"))
BlenderLog.report(LogVisor::FatalError, "unable to open PyOutStream with blender");
2015-07-25 23:01:02 +00:00
}
public:
PyOutStream(const PyOutStream& other) = delete;
PyOutStream(PyOutStream&& other)
2015-09-03 01:43:20 +00:00
: std::ostream(&m_sbuf), m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf))
2015-07-25 23:01:02 +00:00
{other.m_parent = nullptr;}
2015-08-05 22:59:59 +00:00
~PyOutStream() {close();}
void close()
2015-07-25 23:01:02 +00:00
{
2015-08-16 23:01:35 +00:00
if (m_parent && m_parent->m_lock)
2015-07-28 02:25:33 +00:00
{
2015-08-16 23:01:35 +00:00
m_parent->_writeLine("PYEND");
char readBuf[16];
m_parent->_readLine(readBuf, 16);
if (strcmp(readBuf, "DONE"))
BlenderLog.report(LogVisor::FatalError, "unable to close PyOutStream with blender");
m_parent->m_lock = false;
2015-07-28 02:25:33 +00:00
}
2015-07-25 23:01:02 +00:00
}
2015-09-24 01:00:30 +00:00
#if __GNUC__
__attribute__((__format__ (__printf__, 2, 3)))
#endif
2015-08-03 02:05:04 +00:00
void format(const char* fmt, ...)
{
2015-08-16 23:01:35 +00:00
if (!m_parent || !m_parent->m_lock)
2015-08-05 22:59:59 +00:00
BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream::format()");
2015-08-03 02:05:04 +00:00
va_list ap;
va_start(ap, fmt);
char* result = nullptr;
2015-08-31 03:36:24 +00:00
#ifdef _WIN32
int length = _vscprintf(fmt, ap);
result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap);
#else
2015-08-03 02:05:04 +00:00
int length = vasprintf(&result, fmt, ap);
2015-08-31 03:36:24 +00:00
#endif
va_end(ap);
2015-08-03 02:05:04 +00:00
if (length > 0)
this->write(result, length);
free(result);
}
2015-08-31 03:36:24 +00:00
void linkBlend(const std::string& target, const std::string& objName, bool link=true);
class ANIMOutStream
{
BlenderConnection* m_parent;
unsigned m_curCount = 0;
unsigned m_totalCount = 0;
bool m_inCurve = false;
public:
enum CurveType
{
CurveRotate,
CurveTranslate,
CurveScale
};
ANIMOutStream(BlenderConnection* parent)
: m_parent(parent)
{
m_parent->_writeLine("PYANIM");
char readBuf[16];
m_parent->_readLine(readBuf, 16);
if (strcmp(readBuf, "ANIMREADY"))
BlenderLog.report(LogVisor::FatalError, "unable to open ANIMOutStream");
}
~ANIMOutStream()
{
char tp = -1;
m_parent->_writeBuf(&tp, 1);
char readBuf[16];
m_parent->_readLine(readBuf, 16);
if (strcmp(readBuf, "ANIMDONE"))
BlenderLog.report(LogVisor::FatalError, "unable to close ANIMOutStream");
}
void changeCurve(CurveType type, unsigned crvIdx, unsigned keyCount)
{
if (m_curCount != m_totalCount)
BlenderLog.report(LogVisor::FatalError, "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 write(unsigned frame, float val)
{
if (!m_inCurve)
BlenderLog.report(LogVisor::FatalError, "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::FatalError, "ANIMOutStream keyCount overflow");
}
};
ANIMOutStream beginANIMCurve()
{
return ANIMOutStream(m_parent);
}
2015-07-25 23:01:02 +00:00
};
2015-10-02 04:06:45 +00:00
PyOutStream beginPythonOut(bool deleteOnError=false)
2015-07-25 23:01:02 +00:00
{
2015-08-16 23:01:35 +00:00
if (m_lock)
BlenderLog.report(LogVisor::FatalError, "lock already held for BlenderConnection::beginPythonOut()");
2015-08-05 22:59:59 +00:00
return PyOutStream(this, deleteOnError);
2015-07-25 23:01:02 +00:00
}
2015-10-02 04:06:45 +00:00
class DataStream
{
friend class BlenderConnection;
BlenderConnection* m_parent;
DataStream(BlenderConnection* parent)
: m_parent(parent)
{
m_parent->m_lock = true;
m_parent->_writeLine("DATABEGIN");
char readBuf[16];
m_parent->_readLine(readBuf, 16);
if (strcmp(readBuf, "READY"))
BlenderLog.report(LogVisor::FatalError, "unable to open DataStream with blender");
}
public:
DataStream(const DataStream& other) = delete;
DataStream(DataStream&& other)
: m_parent(other.m_parent) {other.m_parent = nullptr;}
~DataStream() {close();}
void close()
{
if (m_parent && m_parent->m_lock)
{
m_parent->_writeLine("DATAEND");
char readBuf[16];
m_parent->_readLine(readBuf, 16);
if (strcmp(readBuf, "DONE"))
BlenderLog.report(LogVisor::FatalError, "unable to close DataStream with blender");
m_parent->m_lock = false;
}
}
std::vector<std::string> getMeshList()
{
m_parent->_writeLine("MESHLIST");
uint32_t count;
m_parent->_readBuf(&count, 4);
std::vector<std::string> retval;
retval.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];
m_parent->_readLine(name, 128);
retval.push_back(name);
}
return retval;
}
/* Intermediate mesh representation prepared by blender from a single mesh object */
struct Mesh
{
2015-10-03 01:53:45 +00:00
struct Vector2f
2015-10-02 04:06:45 +00:00
{
2015-10-09 02:08:10 +00:00
atVec2f val;
Vector2f(BlenderConnection& conn) {conn._readBuf(&val, 8);}
operator const atVec2f&() const {return val;}
2015-10-03 01:53:45 +00:00
};
struct Vector3f
{
2015-10-09 02:08:10 +00:00
atVec3f val;
Vector3f(BlenderConnection& conn) {conn._readBuf(&val, 12);}
operator const atVec3f&() const {return val;}
2015-10-03 01:53:45 +00:00
};
struct Index
{
uint32_t val;
Index(BlenderConnection& conn) {conn._readBuf(&val, 4);}
operator const uint32_t&() const {return val;}
2015-10-03 01:53:45 +00:00
};
2015-10-09 02:08:10 +00:00
/* Cumulative AABB */
Vector3f aabbMin;
Vector3f aabbMax;
/* HECL source of each material */
struct Material
{
2015-10-14 23:06:47 +00:00
std::string name;
2015-10-09 02:08:10 +00:00
std::string source;
std::vector<ProjectPath> texs;
2015-10-17 02:06:27 +00:00
std::unordered_map<std::string, int32_t> iprops;
2015-10-09 02:08:10 +00:00
Material(BlenderConnection& conn);
};
std::vector<std::vector<Material>> materialSets;
/* Vertex buffer data */
2015-10-03 01:53:45 +00:00
std::vector<Vector3f> pos;
std::vector<Vector3f> norm;
uint32_t colorLayerCount = 0;
2015-10-04 04:35:18 +00:00
std::vector<Vector3f> color;
2015-10-03 01:53:45 +00:00
uint32_t uvLayerCount = 0;
2015-10-04 04:35:18 +00:00
std::vector<Vector2f> uv;
2015-10-03 01:53:45 +00:00
/* Skinning data */
std::vector<std::string> boneNames;
struct SkinBind
{
uint32_t boneIdx;
float weight;
SkinBind(BlenderConnection& conn) {conn._readBuf(&boneIdx, 8);}
};
std::vector<std::vector<SkinBind>> skins;
2015-10-02 04:06:45 +00:00
2015-10-03 01:53:45 +00:00
/* Islands of the same material/skinBank are represented here */
struct Surface
{
Vector3f centroid;
Index materialIdx;
Vector3f aabbMin;
Vector3f aabbMax;
Vector3f reflectionNormal;
uint32_t skinBankIdx;
/* Vertex indexing data (all primitives joined as degenerate tri-strip) */
struct Vert
2015-10-02 04:06:45 +00:00
{
2015-10-03 01:53:45 +00:00
uint32_t iPos;
uint32_t iNorm;
uint32_t iColor[4] = {uint32_t(-1)};
uint32_t iUv[8] = {uint32_t(-1)};
uint32_t iSkin;
2015-10-04 04:35:18 +00:00
uint32_t iBankSkin = -1;
2015-10-02 04:06:45 +00:00
2015-10-03 01:53:45 +00:00
Vert(BlenderConnection& conn, const Mesh& parent);
2015-10-02 04:06:45 +00:00
};
2015-10-03 01:53:45 +00:00
std::vector<Vert> verts;
2015-10-02 04:06:45 +00:00
2015-10-04 04:35:18 +00:00
Surface(BlenderConnection& conn, Mesh& parent, int skinSlotCount);
2015-10-02 04:06:45 +00:00
};
2015-10-03 01:53:45 +00:00
std::vector<Surface> surfaces;
2015-10-04 04:35:18 +00:00
struct SkinBanks
2015-10-03 01:53:45 +00:00
{
std::vector<std::vector<uint32_t>> banks;
2015-10-04 04:35:18 +00:00
std::vector<std::vector<uint32_t>>::iterator addSkinBank(int skinSlotCount)
2015-10-03 01:53:45 +00:00
{
2015-10-04 04:35:18 +00:00
banks.emplace_back();
if (skinSlotCount > 0)
banks.back().reserve(skinSlotCount);
2015-10-04 04:35:18 +00:00
return banks.end() - 1;
2015-10-03 01:53:45 +00:00
}
2015-10-04 04:35:18 +00:00
uint32_t addSurface(const Surface& surf, int skinSlotCount);
2015-10-03 01:53:45 +00:00
} skinBanks;
2015-10-02 04:06:45 +00:00
2015-10-04 04:35:18 +00:00
Mesh(BlenderConnection& conn, int skinSlotCount);
2015-10-02 04:06:45 +00:00
};
2015-10-04 04:35:18 +00:00
/* Compile mesh by context */
Mesh compileMesh(int skinSlotCount=10)
{
char req[128];
snprintf(req, 128, "MESHCOMPILE %d", skinSlotCount);
m_parent->_writeLine(req);
char readBuf[256];
m_parent->_readLine(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(LogVisor::FatalError, "unable to cook mesh: %s", readBuf);
return Mesh(*m_parent, skinSlotCount);
}
2015-10-02 04:06:45 +00:00
/* Compile mesh by name */
2015-10-04 04:35:18 +00:00
Mesh compileMesh(const std::string& name, int skinSlotCount=10)
2015-10-02 04:06:45 +00:00
{
char req[128];
2015-10-04 04:35:18 +00:00
snprintf(req, 128, "MESHCOMPILENAME %s %d", name.c_str(), skinSlotCount);
2015-10-02 04:06:45 +00:00
m_parent->_writeLine(req);
char readBuf[256];
m_parent->_readLine(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(LogVisor::FatalError, "unable to cook mesh '%s': %s", name.c_str(), readBuf);
2015-10-04 04:35:18 +00:00
return Mesh(*m_parent, skinSlotCount);
2015-10-02 04:06:45 +00:00
}
/* Compile all meshes into one */
2015-10-07 01:16:54 +00:00
Mesh compileAllMeshes(int skinSlotCount=10, float maxOctantLength=5.0)
2015-10-02 04:06:45 +00:00
{
char req[128];
2015-10-07 01:16:54 +00:00
snprintf(req, 128, "MESHCOMPILEALL %d %f", skinSlotCount, maxOctantLength);
2015-10-02 04:06:45 +00:00
m_parent->_writeLine(req);
char readBuf[256];
m_parent->_readLine(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(LogVisor::FatalError, "unable to cook all meshes: %s", readBuf);
2015-10-04 04:35:18 +00:00
return Mesh(*m_parent, skinSlotCount);
2015-10-02 04:06:45 +00:00
}
};
DataStream beginData()
{
if (m_lock)
BlenderLog.report(LogVisor::FatalError, "lock already held for BlenderConnection::beginDataIn()");
return DataStream(this);
}
2015-05-24 04:51:16 +00:00
void quitBlender();
2015-07-28 02:25:33 +00:00
2015-10-02 04:06:45 +00:00
static BlenderConnection& SharedConnection()
2015-07-28 02:25:33 +00:00
{
if (!SharedBlenderConnection)
2015-09-22 01:41:38 +00:00
SharedBlenderConnection = new BlenderConnection(HECL::VerbosityLevel);
2015-07-28 02:25:33 +00:00
return *SharedBlenderConnection;
}
2015-10-02 04:06:45 +00:00
void closeStream()
2015-08-16 23:01:35 +00:00
{
if (m_lock)
deleteBlend();
}
2015-10-02 04:06:45 +00:00
static void Shutdown()
2015-07-28 02:25:33 +00:00
{
2015-08-16 23:01:35 +00:00
if (SharedBlenderConnection)
{
SharedBlenderConnection->closeStream();
SharedBlenderConnection->quitBlender();
delete SharedBlenderConnection;
SharedBlenderConnection = nullptr;
BlenderLog.report(LogVisor::Info, "BlenderConnection Shutdown Successful");
}
2015-07-28 02:25:33 +00:00
}
2015-08-16 23:01:35 +00:00
};
2015-07-28 02:25:33 +00:00
}
2015-07-22 19:14:50 +00:00
#endif // BLENDERCONNECTION_HPP