mirror of https://github.com/AxioDL/metaforce.git
Integration work on mesh optimization
This commit is contained in:
parent
bebeffb247
commit
ab5451ea45
|
@ -456,27 +456,22 @@ void BlenderConnection::PyOutStream::linkBlend(const std::string& target,
|
||||||
objName.c_str(), objName.c_str(), target.c_str(), objName.c_str());
|
objName.c_str(), objName.c_str(), target.c_str(), objName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
BlenderConnection::DataStream::Mesh::Mesh(BlenderConnection& conn)
|
BlenderConnection::DataStream::Mesh::Mesh(BlenderConnection& conn, int maxSkinBanks)
|
||||||
{
|
{
|
||||||
uint32_t matCount;
|
uint32_t matSetCount;
|
||||||
conn._readBuf(&matCount, 4);
|
conn._readBuf(&matSetCount, 4);
|
||||||
materials.reserve(matCount);
|
materialSets.reserve(matSetCount);
|
||||||
for (int i=0 ; i<matCount ; ++i)
|
for (int i=0 ; i<matSetCount ; ++i)
|
||||||
{
|
{
|
||||||
char mat[2048];
|
materialSets.emplace_back();
|
||||||
conn._readLine(mat, 2048);
|
std::vector<Material>& materials = materialSets.back();
|
||||||
materials.push_back(mat);
|
uint32_t matCount;
|
||||||
|
conn._readBuf(&matCount, 4);
|
||||||
|
materials.reserve(matCount);
|
||||||
|
for (int i=0 ; i<matCount ; ++i)
|
||||||
|
materials.emplace_back(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t submeshCount;
|
|
||||||
conn._readBuf(&submeshCount, 4);
|
|
||||||
submeshes.reserve(submeshCount);
|
|
||||||
for (int i=0 ; i<submeshCount ; ++i)
|
|
||||||
submeshes.emplace_back(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlenderConnection::DataStream::Mesh::Submesh::Submesh(BlenderConnection& conn)
|
|
||||||
{
|
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
conn._readBuf(&count, 4);
|
conn._readBuf(&count, 4);
|
||||||
pos.reserve(count);
|
pos.reserve(count);
|
||||||
|
@ -528,39 +523,53 @@ BlenderConnection::DataStream::Mesh::Submesh::Submesh(BlenderConnection& conn)
|
||||||
binds.emplace_back(conn);
|
binds.emplace_back(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn._readBuf(&count, 4);
|
uint8_t isSurf;
|
||||||
skinBanks.reserve(count);
|
conn._readBuf(&isSurf, 1);
|
||||||
for (int i=0 ; i<count ; ++i)
|
while (isSurf)
|
||||||
{
|
{
|
||||||
skinBanks.emplace_back();
|
surfaces.emplace_back(conn, *this);
|
||||||
std::vector<Index>& bank = skinBanks.back();
|
conn._readBuf(&isSurf, 1);
|
||||||
uint32_t idxCount;
|
|
||||||
conn._readBuf(&idxCount, 4);
|
|
||||||
bank.reserve(idxCount);
|
|
||||||
for (int j=0 ; j<idxCount ; ++j)
|
|
||||||
bank.emplace_back(conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn._readBuf(&count, 4);
|
/* Resolve skin banks here */
|
||||||
surfaces.reserve(count);
|
if (boneNames.size())
|
||||||
for (int i=0 ; i<count ; ++i)
|
for (Surface& surf : surfaces)
|
||||||
surfaces.emplace_back(conn, *this);
|
skinBanks.addSurface(surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlenderConnection::DataStream::Mesh::Submesh::Surface::Surface
|
BlenderConnection::DataStream::Mesh::Material::Material
|
||||||
(BlenderConnection& conn, const Submesh& parent)
|
(BlenderConnection& conn)
|
||||||
: centroid(conn), materialIdx(conn), aabbMin(conn), aabbMax(conn),
|
|
||||||
reflectionNormal(conn), skinBankIdx(conn)
|
|
||||||
{
|
{
|
||||||
uint32_t count;
|
char buf[4096];
|
||||||
conn._readBuf(&count, 4);
|
conn._readLine(buf, 4096);
|
||||||
verts.reserve(count);
|
source.assign(buf);
|
||||||
for (int i=0 ; i<count ; ++i)
|
|
||||||
verts.emplace_back(conn, parent);
|
uint32_t texCount;
|
||||||
|
conn._readBuf(&texCount, 4);
|
||||||
|
texs.reserve(texCount);
|
||||||
|
for (int i=0 ; i<texCount ; ++i)
|
||||||
|
{
|
||||||
|
conn._readLine(buf, 4096);
|
||||||
|
texs.emplace_back(buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BlenderConnection::DataStream::Mesh::Submesh::Surface::Vert::Vert
|
BlenderConnection::DataStream::Mesh::Surface::Surface
|
||||||
(BlenderConnection& conn, const Submesh& parent)
|
(BlenderConnection& conn, const Mesh& parent)
|
||||||
|
: centroid(conn), materialIdx(conn), aabbMin(conn), aabbMax(conn),
|
||||||
|
reflectionNormal(conn)
|
||||||
|
{
|
||||||
|
uint8_t isVert;
|
||||||
|
conn._readBuf(&isVert, 1);
|
||||||
|
while (isVert)
|
||||||
|
{
|
||||||
|
verts.emplace_back(conn, parent);
|
||||||
|
conn._readBuf(&isVert, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlenderConnection::DataStream::Mesh::Surface::Vert::Vert
|
||||||
|
(BlenderConnection& conn, const Mesh& parent)
|
||||||
{
|
{
|
||||||
conn._readBuf(&iPos, 4);
|
conn._readBuf(&iPos, 4);
|
||||||
conn._readBuf(&iNorm, 4);
|
conn._readBuf(&iNorm, 4);
|
||||||
|
|
|
@ -282,90 +282,93 @@ public:
|
||||||
struct Mesh
|
struct Mesh
|
||||||
{
|
{
|
||||||
/* HECL source of each material */
|
/* HECL source of each material */
|
||||||
std::vector<std::string> materials;
|
struct Material
|
||||||
|
|
||||||
/* Encapsulates mesh data up to maximum indexing space,
|
|
||||||
* overflowing to additional Submeshes as needed */
|
|
||||||
struct Submesh
|
|
||||||
{
|
{
|
||||||
/* Vertex buffer data */
|
std::string source;
|
||||||
struct Vector2f
|
std::vector<std::string> texs;
|
||||||
{
|
|
||||||
float val[2];
|
|
||||||
Vector2f(BlenderConnection& conn) {conn._readBuf(val, 8);}
|
|
||||||
};
|
|
||||||
struct Vector3f
|
|
||||||
{
|
|
||||||
float val[3];
|
|
||||||
Vector3f(BlenderConnection& conn) {conn._readBuf(val, 12);}
|
|
||||||
};
|
|
||||||
struct Vector4f
|
|
||||||
{
|
|
||||||
float val[4];
|
|
||||||
Vector4f(BlenderConnection& conn) {conn._readBuf(val, 16);}
|
|
||||||
};
|
|
||||||
struct Index
|
|
||||||
{
|
|
||||||
uint32_t val;
|
|
||||||
Index(BlenderConnection& conn) {conn._readBuf(&val, 4);}
|
|
||||||
};
|
|
||||||
std::vector<Vector3f> pos;
|
|
||||||
std::vector<Vector3f> norm;
|
|
||||||
uint32_t colorLayerCount = 0;
|
|
||||||
std::vector<Vector4f> color[4];
|
|
||||||
uint32_t uvLayerCount = 0;
|
|
||||||
std::vector<Vector2f> uv[8];
|
|
||||||
|
|
||||||
/* Skinning data */
|
Material(BlenderConnection& conn);
|
||||||
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;
|
|
||||||
std::vector<std::vector<Index>> skinBanks;
|
|
||||||
|
|
||||||
/* Islands of the same material/skinBank are represented here */
|
|
||||||
struct Surface
|
|
||||||
{
|
|
||||||
Vector3f centroid;
|
|
||||||
Index materialIdx;
|
|
||||||
Vector3f aabbMin;
|
|
||||||
Vector3f aabbMax;
|
|
||||||
Vector3f reflectionNormal;
|
|
||||||
Index skinBankIdx;
|
|
||||||
|
|
||||||
/* Vertex indexing data */
|
|
||||||
struct Vert
|
|
||||||
{
|
|
||||||
uint32_t iPos;
|
|
||||||
uint32_t iNorm;
|
|
||||||
uint32_t iColor[4] = {uint32_t(-1)};
|
|
||||||
uint32_t iUv[8] = {uint32_t(-1)};
|
|
||||||
uint32_t iSkin;
|
|
||||||
|
|
||||||
Vert(BlenderConnection& conn, const Submesh& parent);
|
|
||||||
};
|
|
||||||
std::vector<Vert> verts;
|
|
||||||
|
|
||||||
Surface(BlenderConnection& conn, const Submesh& parent);
|
|
||||||
};
|
|
||||||
std::vector<Surface> surfaces;
|
|
||||||
|
|
||||||
Submesh(BlenderConnection& conn);
|
|
||||||
};
|
};
|
||||||
std::vector<Submesh> submeshes;
|
std::vector<std::vector<Material>> materialSets;
|
||||||
|
|
||||||
Mesh(BlenderConnection& conn);
|
/* Vertex buffer data */
|
||||||
|
struct Vector2f
|
||||||
|
{
|
||||||
|
float val[2];
|
||||||
|
Vector2f(BlenderConnection& conn) {conn._readBuf(val, 8);}
|
||||||
|
};
|
||||||
|
struct Vector3f
|
||||||
|
{
|
||||||
|
float val[3];
|
||||||
|
Vector3f(BlenderConnection& conn) {conn._readBuf(val, 12);}
|
||||||
|
};
|
||||||
|
struct Index
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
Index(BlenderConnection& conn) {conn._readBuf(&val, 4);}
|
||||||
|
};
|
||||||
|
std::vector<Vector3f> pos;
|
||||||
|
std::vector<Vector3f> norm;
|
||||||
|
uint32_t colorLayerCount = 0;
|
||||||
|
std::vector<Vector3f> color[4];
|
||||||
|
uint32_t uvLayerCount = 0;
|
||||||
|
std::vector<Vector2f> uv[8];
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
{
|
||||||
|
uint32_t iPos;
|
||||||
|
uint32_t iNorm;
|
||||||
|
uint32_t iColor[4] = {uint32_t(-1)};
|
||||||
|
uint32_t iUv[8] = {uint32_t(-1)};
|
||||||
|
uint32_t iSkin;
|
||||||
|
|
||||||
|
Vert(BlenderConnection& conn, const Mesh& parent);
|
||||||
|
};
|
||||||
|
std::vector<Vert> verts;
|
||||||
|
|
||||||
|
Surface(BlenderConnection& conn, const Mesh& parent);
|
||||||
|
};
|
||||||
|
std::vector<Surface> surfaces;
|
||||||
|
|
||||||
|
class SkinBanks
|
||||||
|
{
|
||||||
|
std::vector<std::vector<uint32_t>> banks;
|
||||||
|
public:
|
||||||
|
uint32_t addSurface(const Surface& surf)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} skinBanks;
|
||||||
|
|
||||||
|
Mesh(BlenderConnection& conn, int maxSkinBanks);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Compile mesh by name */
|
/* Compile mesh by name */
|
||||||
Mesh compileMesh(const std::string& name, int maxIdx=65535, int maxSkinBanks=10)
|
Mesh compileMesh(const std::string& name, int maxSkinBanks=10)
|
||||||
{
|
{
|
||||||
char req[128];
|
char req[128];
|
||||||
snprintf(req, 128, "MESHCOMPILE %s %d %d", name.c_str(), maxIdx, maxSkinBanks);
|
snprintf(req, 128, "MESHCOMPILE %s %d", name.c_str(), maxSkinBanks);
|
||||||
m_parent->_writeLine(req);
|
m_parent->_writeLine(req);
|
||||||
|
|
||||||
char readBuf[256];
|
char readBuf[256];
|
||||||
|
@ -373,14 +376,14 @@ public:
|
||||||
if (strcmp(readBuf, "OK"))
|
if (strcmp(readBuf, "OK"))
|
||||||
BlenderLog.report(LogVisor::FatalError, "unable to cook mesh '%s': %s", name.c_str(), readBuf);
|
BlenderLog.report(LogVisor::FatalError, "unable to cook mesh '%s': %s", name.c_str(), readBuf);
|
||||||
|
|
||||||
return Mesh(*m_parent);
|
return Mesh(*m_parent, maxSkinBanks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile all meshes into one */
|
/* Compile all meshes into one */
|
||||||
Mesh compileAllMeshes(int maxIdx=65535, int maxSkinBanks=10)
|
Mesh compileAllMeshes(int maxSkinBanks=10)
|
||||||
{
|
{
|
||||||
char req[128];
|
char req[128];
|
||||||
snprintf(req, 128, "MESHCOMPILEALL %d %d", maxIdx, maxSkinBanks);
|
snprintf(req, 128, "MESHCOMPILEALL %d", maxSkinBanks);
|
||||||
m_parent->_writeLine(req);
|
m_parent->_writeLine(req);
|
||||||
|
|
||||||
char readBuf[256];
|
char readBuf[256];
|
||||||
|
@ -388,7 +391,7 @@ public:
|
||||||
if (strcmp(readBuf, "OK"))
|
if (strcmp(readBuf, "OK"))
|
||||||
BlenderLog.report(LogVisor::FatalError, "unable to cook all meshes: %s", readBuf);
|
BlenderLog.report(LogVisor::FatalError, "unable to cook all meshes: %s", readBuf);
|
||||||
|
|
||||||
return Mesh(*m_parent);
|
return Mesh(*m_parent, maxSkinBanks);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
DataStream beginData()
|
DataStream beginData()
|
||||||
|
|
|
@ -6,7 +6,6 @@ list(APPEND PY_SOURCES
|
||||||
hecl/hmdl/HMDLMesh.py
|
hecl/hmdl/HMDLMesh.py
|
||||||
hecl/hmdl/HMDLShader.py
|
hecl/hmdl/HMDLShader.py
|
||||||
hecl/hmdl/HMDLSkin.py
|
hecl/hmdl/HMDLSkin.py
|
||||||
hecl/hmdl/HMDLTxtr.py
|
|
||||||
hecl/sact/__init__.py
|
hecl/sact/__init__.py
|
||||||
hecl/sact/SACTAction.py
|
hecl/sact/SACTAction.py
|
||||||
hecl/sact/SACTEvent.py
|
hecl/sact/SACTEvent.py
|
||||||
|
|
|
@ -19,10 +19,10 @@ from bpy.app.handlers import persistent
|
||||||
|
|
||||||
# Appendable list allowing external addons to register additional resource types
|
# Appendable list allowing external addons to register additional resource types
|
||||||
hecl_typeS = [
|
hecl_typeS = [
|
||||||
('NONE', "None", "Active scene not using HECL", None, None),
|
('NONE', "None", "Active scene not using HECL", None),
|
||||||
('MESH', "Mesh", "Active scene represents an HMDL Mesh", hmdl.draw, hmdl.cook),
|
('MESH', "Mesh", "Active scene represents an HMDL Mesh", hmdl.draw),
|
||||||
('ACTOR', "Actor", "Active scene represents a HECL Actor", sact.draw, sact.cook),
|
('ACTOR', "Actor", "Active scene represents a HECL Actor", sact.draw),
|
||||||
('AREA', "Area", "Active scene represents a HECL Area", srea.draw, srea.cook)]
|
('AREA', "Area", "Active scene represents a HECL Area", srea.draw)]
|
||||||
|
|
||||||
# Main Scene Panel
|
# Main Scene Panel
|
||||||
class hecl_scene_panel(bpy.types.Panel):
|
class hecl_scene_panel(bpy.types.Panel):
|
||||||
|
@ -47,15 +47,6 @@ class hecl_scene_panel(bpy.types.Panel):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# Blender-selected polymorphism cook
|
|
||||||
def do_cook(writebuf, platform_type, endian_char):
|
|
||||||
for tp in hecl_typeS:
|
|
||||||
if tp[0] == bpy.context.scene.hecl_type:
|
|
||||||
if callable(tp[4]):
|
|
||||||
return tp[4](writefd, platform_type, endian_char)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# Blender export-type registration
|
# Blender export-type registration
|
||||||
def register_export_type_enum():
|
def register_export_type_enum():
|
||||||
bpy.types.Scene.hecl_type = bpy.props.EnumProperty(items=
|
bpy.types.Scene.hecl_type = bpy.props.EnumProperty(items=
|
||||||
|
@ -74,20 +65,7 @@ def add_export_type(type_tuple):
|
||||||
|
|
||||||
# Shell command receiver (from HECL driver)
|
# Shell command receiver (from HECL driver)
|
||||||
def command(cmdline, writepipeline, writepipebuf):
|
def command(cmdline, writepipeline, writepipebuf):
|
||||||
if cmdline[0] == b'COOK':
|
pass
|
||||||
resource_type = bpy.context.scene.hecl_type.encode()
|
|
||||||
writepipeline(resource_type)
|
|
||||||
ackbytes = readpipeline()
|
|
||||||
if ackbytes != b'ACK':
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
result = do_cook(writepipebuf, cmdline[1].decode(), cmdline[2].decode())
|
|
||||||
if result == None or result == True:
|
|
||||||
writepipeline(b'SUCCESS')
|
|
||||||
else:
|
|
||||||
writepipeline(b'FAILURE')
|
|
||||||
except:
|
|
||||||
writepipeline(b'EXCEPTION')
|
|
||||||
|
|
||||||
# Load scene callback
|
# Load scene callback
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
|
|
|
@ -3,528 +3,316 @@ HMDL Export Blender Addon
|
||||||
By Jack Andersen <jackoalan@gmail.com>
|
By Jack Andersen <jackoalan@gmail.com>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import struct
|
import bpy, bmesh, operator, struct
|
||||||
import bpy
|
from mathutils import Vector
|
||||||
|
|
||||||
class loop_vert:
|
# Class for building unique sets of vertex attributes for VBO generation
|
||||||
|
class VertPool:
|
||||||
|
pos = {}
|
||||||
|
norm = {}
|
||||||
|
skin = {}
|
||||||
|
color = []
|
||||||
|
uv = []
|
||||||
|
dlay = None
|
||||||
|
clays = []
|
||||||
|
ulays = []
|
||||||
|
|
||||||
def __init__(self, mesh, loop):
|
# Initialize hash-unique index for each available attribute
|
||||||
self.mesh = mesh
|
def __init__(self, bm):
|
||||||
self.loop = loop
|
dlay = None
|
||||||
|
if len(bm.verts.layers.deform):
|
||||||
|
dlay = bm.verts.layers.deform[0]
|
||||||
|
self.dlay = dlay
|
||||||
|
|
||||||
def __hash__(self):
|
clays = []
|
||||||
return (self.mesh, self.loop.index).__hash__()
|
for cl in range(len(bm.loops.layers.color)):
|
||||||
|
clays.append(bm.loops.layers.color[cl])
|
||||||
|
self.color.append([])
|
||||||
|
self.clays = clays
|
||||||
|
|
||||||
def __eq__(self, other):
|
ulays = []
|
||||||
return self.mesh == other.mesh and self.loop.index == other.loop.index
|
for ul in range(len(bm.loops.layers.uv)):
|
||||||
|
ulays.append(bm.loops.layers.uv[ul])
|
||||||
|
self.uv.append([])
|
||||||
|
self.ulays = ulays
|
||||||
|
|
||||||
def __ne__(self, other):
|
# Per-vert pool attributes
|
||||||
return self.mesh != other.mesh or self.loop.index != other.loop.index
|
for v in bm.verts:
|
||||||
|
pf = v.co.copy().freeze()
|
||||||
|
if pf not in self.pos:
|
||||||
|
self.pos[pf] = len(self.pos)
|
||||||
|
nf = v.normal.copy().freeze()
|
||||||
|
if nf not in self.norm:
|
||||||
|
self.norm[nf] = len(self.norm)
|
||||||
|
if dlay:
|
||||||
|
sf = tuple(sorted(v[dlay].items()))
|
||||||
|
if sf not in self.skin:
|
||||||
|
self.skin[sf] = len(self.skin)
|
||||||
|
|
||||||
def __str__(self):
|
# Per-loop pool attributes
|
||||||
return (self.mesh, self.loop.index).__str__()
|
for f in bm.faces:
|
||||||
|
for l in f.loops:
|
||||||
|
for cl in range(len(clays)):
|
||||||
|
cf = l[clays[cl]].copy().freeze()
|
||||||
|
if cf not in self.color[cl]:
|
||||||
|
self.color[cl][cf] = len(self.color[cl])
|
||||||
|
for ul in range(len(ulays)):
|
||||||
|
uf = l[ulays[ul]].uv.copy().freeze()
|
||||||
|
if uf not in self.uv[ul]:
|
||||||
|
self.uv[ul][uf] = len(self.uv[ul])
|
||||||
|
|
||||||
|
def write_out(self, writebuffunc, vert_groups):
|
||||||
|
writebuffunc(struct.pack('I', len(self.pos)))
|
||||||
|
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuffunc(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
||||||
|
|
||||||
# Round up to nearest 32 multiple
|
writebuffunc(struct.pack('I', len(self.norm)))
|
||||||
def ROUND_UP_32(num):
|
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
|
||||||
return (num + 31) & ~31
|
writebuffunc(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
|
||||||
|
|
||||||
# Round up to nearest 4 multiple
|
writebuffunc(struct.pack('I', len(self.color)))
|
||||||
def ROUND_UP_4(num):
|
for clay in self.color:
|
||||||
return (num + 3) & ~3
|
writebuffunc(struct.pack('I', len(clay)))
|
||||||
|
for c in sorted(clay.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuffunc(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
|
||||||
|
|
||||||
# This routine conditionally inserts a loop into a multi-tiered
|
writebuffunc(struct.pack('I', len(self.uv)))
|
||||||
# array/set collection; simultaneously relating verts to loops and
|
for ulay in self.uv:
|
||||||
# eliminating redundant loops (containing identical UV coordinates)
|
writebuffunc(struct.pack('I', len(ulay)))
|
||||||
def _augment_loop_vert_array(lv_array, mesh, loop, uv_count):
|
for u in sorted(ulay.items(), key=operator.itemgetter(1)):
|
||||||
|
writebuffunc(struct.pack('ff', u[0][0], u[0][1]))
|
||||||
|
|
||||||
# Create loop_vert object for comparitive testing
|
writebuffunc(struct.pack('I', len(vert_groups)))
|
||||||
lv = loop_vert(mesh, loop)
|
for vgrp in vert_groups:
|
||||||
|
writebuffunc((vgrp.name + '\n').encode())
|
||||||
|
|
||||||
# First perform quick check to see if loop is already in a set
|
writebuffunc(struct.pack('I', len(self.skin)))
|
||||||
for existing_loop_set in lv_array:
|
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
|
||||||
if lv in existing_loop_set:
|
entries = s[0]
|
||||||
return
|
writebuffunc(struct.pack('I', len(entries)))
|
||||||
|
for ent in entries:
|
||||||
|
writebuffunc(struct.pack('If', ent[0], ent[1]))
|
||||||
|
|
||||||
# Now perform extended check to see if any UV coordinate values already match
|
def set_bm_layers(self, bm):
|
||||||
for existing_loop_set in lv_array:
|
self.dlay = None
|
||||||
for existing_loop in existing_loop_set:
|
if len(bm.verts.layers.deform):
|
||||||
matches = True
|
self.dlay = bm.verts.layers.deform[0]
|
||||||
for uv_layer_idx in range(uv_count):
|
|
||||||
uv_layer = mesh.uv_layers[uv_layer_idx]
|
|
||||||
existing_uv_coords = uv_layer.data[existing_loop.loop.index].uv
|
|
||||||
check_uv_coords = uv_layer.data[loop.index].uv
|
|
||||||
if (existing_uv_coords[0] != check_uv_coords[0] or
|
|
||||||
existing_uv_coords[1] != check_uv_coords[1]):
|
|
||||||
matches = False
|
|
||||||
break
|
|
||||||
if matches:
|
|
||||||
existing_loop_set.append(lv)
|
|
||||||
return
|
|
||||||
|
|
||||||
# If we get here, no match found; add new set to `lv_array`
|
clays = []
|
||||||
lv_array.append([lv])
|
for cl in range(len(bm.loops.layers.color)):
|
||||||
|
clays.append(bm.loops.layers.color[cl])
|
||||||
|
self.clays = clays
|
||||||
|
|
||||||
|
ulays = []
|
||||||
|
for ul in range(len(bm.loops.layers.uv)):
|
||||||
|
ulays.append(bm.loops.layers.uv[ul])
|
||||||
|
self.ulays = ulays
|
||||||
|
|
||||||
# Get loop set from collection generated with above method;
|
def get_pos_idx(self, vert):
|
||||||
# containing a specified loop
|
pf = vert.co.copy().freeze()
|
||||||
def _get_loop_set(lv_array, mesh, loop):
|
return self.pos[pf]
|
||||||
|
|
||||||
# Create loop_vert object for comparitive testing
|
def get_norm_idx(self, vert):
|
||||||
lv = loop_vert(mesh, loop)
|
nf = vert.normal.copy().freeze()
|
||||||
|
return self.norm[nf]
|
||||||
|
|
||||||
for existing_loop_set in lv_array:
|
def get_skin_idx(self, vert):
|
||||||
if lv in existing_loop_set:
|
sf = tuple(sorted(vert[self.dlay].items()))
|
||||||
return existing_loop_set
|
return self.skin[sf]
|
||||||
return None
|
|
||||||
|
|
||||||
|
def get_color_idx(self, loop, cidx):
|
||||||
|
cf = tuple(sorted(loop[self.clays[cidx]].items()))
|
||||||
|
return self.color[cidx][cf]
|
||||||
|
|
||||||
# Method to find triangle opposite another triangle over two vert-indices
|
def get_uv_idx(self, loop, uidx):
|
||||||
def _find_polygon_opposite_idxs(mesh, original_triangle, a_idx, b_idx):
|
uf = tuple(sorted(loop[self.ulays[uidx]].items()))
|
||||||
|
return self.uv[uidx][uf]
|
||||||
|
|
||||||
for triangle in mesh.polygons:
|
def loop_out(self, writebuffunc, loop):
|
||||||
|
writebuffunc(struct.pack('BII', 1, self.get_pos_idx(loop.vert), self.get_norm_idx(loop.vert)))
|
||||||
|
for cl in range(len(self.clays)):
|
||||||
|
writebuffunc('I', self.get_color_idx(loop, cl))
|
||||||
|
for ul in range(len(self.ulays)):
|
||||||
|
writebuffunc('I', self.get_uv_idx(loop, ul))
|
||||||
|
writebuffunc(struct.pack('I', self.get_skin_idx(loop.vert)))
|
||||||
|
|
||||||
if triangle == original_triangle:
|
def recursive_faces_islands(dlay, list_out, rem_list, skin_slot_set, skin_slot_count, face):
|
||||||
|
if face not in rem_list:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if dlay:
|
||||||
|
for v in face.verts:
|
||||||
|
sg = tuple(sorted(v[dlay].items()))
|
||||||
|
if sg not in skin_slot_set and len(skin_slot_set) == skin_slot_count:
|
||||||
|
return False
|
||||||
|
skin_slot_set.add(sg)
|
||||||
|
|
||||||
|
list_out.append(face)
|
||||||
|
rem_list.remove(face)
|
||||||
|
for e in face.edges:
|
||||||
|
if not e.is_contiguous:
|
||||||
continue
|
continue
|
||||||
|
for f in e.link_faces:
|
||||||
if (a_idx in triangle.vertices and b_idx in triangle.vertices):
|
if f == face:
|
||||||
return triangle
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Method to find triangle opposite another triangle over two loop-vert sets
|
|
||||||
def _find_polygon_opposite_lvs(mesh, original_triangle, lv_a, lv_b):
|
|
||||||
a_idx = lv_a[0].loop.vertex_index
|
|
||||||
b_idx = lv_b[0].loop.vertex_index
|
|
||||||
return _find_polygon_opposite_idxs(mesh, original_triangle, a_idx, b_idx)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class hmdl_mesh:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
# 4-byte ID string used in generated HMDL file
|
|
||||||
self.file_identifier = '_GEN'
|
|
||||||
|
|
||||||
# Array that holds collections. A collection is a 16-bit index
|
|
||||||
# worth of vertices, elements referencing them, and a
|
|
||||||
# primitive array to draw them
|
|
||||||
self.collections = []
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# If vertex index space is exceeded for a single additional vertex,
|
|
||||||
# a new collection is created and returned by this routine
|
|
||||||
def _check_collection_overflow(self, mesh, collection, rigger, uv_count):
|
|
||||||
max_bone_count = 0;
|
|
||||||
if rigger:
|
|
||||||
max_bone_count = rigger.max_bone_count
|
|
||||||
if not collection or len(collection['vertices']) >= 65535:
|
|
||||||
new_collection = {'uv_count':uv_count, 'max_bone_count':max_bone_count, 'vertices':[], 'vert_weights':[], 'tri_strips':[]}
|
|
||||||
self.collections.append(new_collection)
|
|
||||||
return new_collection, True
|
|
||||||
else:
|
|
||||||
return collection, False
|
|
||||||
|
|
||||||
|
|
||||||
# Augments draw generator with a single blender MESH data object
|
|
||||||
def add_mesh(self, mesh, rigger, uv_count):
|
|
||||||
max_bone_count = 0;
|
|
||||||
if rigger:
|
|
||||||
max_bone_count = rigger.max_bone_count
|
|
||||||
|
|
||||||
print("Optimizing mesh:", mesh.name)
|
|
||||||
opt_gpu_vert_count = 0
|
|
||||||
|
|
||||||
# First, generate compressed loop-vertex array-array-set collection
|
|
||||||
loop_vert_array = []
|
|
||||||
for vert in mesh.vertices:
|
|
||||||
loop_verts = []
|
|
||||||
for loop in mesh.loops:
|
|
||||||
if loop.vertex_index == vert.index:
|
|
||||||
_augment_loop_vert_array(loop_verts, mesh, loop, uv_count)
|
|
||||||
loop_vert_array.append(loop_verts)
|
|
||||||
|
|
||||||
|
|
||||||
# Find best collection to add mesh data into
|
|
||||||
best_collection = None
|
|
||||||
for collection in self.collections:
|
|
||||||
if (collection['uv_count'] == uv_count and
|
|
||||||
collection['max_bone_count'] == max_bone_count and
|
|
||||||
len(collection['vertices']) < 65000):
|
|
||||||
best_collection = collection
|
|
||||||
break
|
|
||||||
if not best_collection:
|
|
||||||
# Create a new one if no good one found
|
|
||||||
best_collection, is_new_collection = self._check_collection_overflow(mesh, None, rigger, uv_count)
|
|
||||||
|
|
||||||
# If rigging, start an array of bone names to be bound to contiguous tri-strips
|
|
||||||
tri_strip_bones = []
|
|
||||||
tri_strip_bones_overflow = False
|
|
||||||
|
|
||||||
# Now begin generating draw primitives
|
|
||||||
visited_polys = set()
|
|
||||||
for poly in mesh.polygons:
|
|
||||||
# Skip if already visited
|
|
||||||
if poly in visited_polys:
|
|
||||||
continue
|
continue
|
||||||
|
if recursive_faces_islands(dlay, list_out, rem_list, skin_slot_set, skin_slot_count, f) == False:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def find_opposite_edge(face, boot_edge, boot_edge2, last_edge, last_edge_2):
|
||||||
|
if last_edge_2:
|
||||||
|
for e in face.edges:
|
||||||
|
if e.verts[0] in last_edge_2.verts or e.verts[1] in last_edge_2.verts:
|
||||||
|
continue
|
||||||
|
return e
|
||||||
|
elif last_edge:
|
||||||
|
return boot_edge2
|
||||||
|
else:
|
||||||
|
return boot_edge
|
||||||
|
|
||||||
|
def recursive_faces_strip(list_out, rem_list, face, boot_edge, boot_edge_2, last_edge, last_edge_2):
|
||||||
|
if face not in rem_list:
|
||||||
|
return
|
||||||
|
list_out.append(face)
|
||||||
|
rem_list.remove(face)
|
||||||
|
edge = find_opposite_edge(face, boot_edge, boot_edge_2, last_edge, last_edge_2)
|
||||||
|
if not edge:
|
||||||
|
return
|
||||||
|
for f in edge.link_faces:
|
||||||
|
if f == face:
|
||||||
|
continue
|
||||||
|
recursive_faces_strip(list_out, rem_list, f, boot_edge, boot_edge_2, edge, last_edge)
|
||||||
|
break
|
||||||
|
|
||||||
|
def count_contiguous_edges(face):
|
||||||
|
retval = 0
|
||||||
|
for e in face.edges:
|
||||||
|
if e.is_contiguous:
|
||||||
|
retval += 1
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def find_loop_opposite_from_other_face(face, other_face):
|
||||||
|
for e in face.edges:
|
||||||
|
if e in other_face.edges:
|
||||||
|
edge = e
|
||||||
|
break
|
||||||
|
for l in face.loops:
|
||||||
|
if l.vert in edge.verts:
|
||||||
|
continue
|
||||||
|
return l
|
||||||
|
|
||||||
|
def stripify_primitive(writebuffunc, vert_pool, prim_faces, last_loop, last_idx):
|
||||||
|
if last_loop:
|
||||||
|
vert_pool.loop_out(writebuffunc, last_loop)
|
||||||
|
last_idx += 1
|
||||||
|
|
||||||
|
if len(prim_faces) == 1:
|
||||||
|
loop = prim_faces[0].loops[0]
|
||||||
|
if last_loop:
|
||||||
|
vert_pool.loop_out(writebuffunc, loop)
|
||||||
|
last_idx += 1
|
||||||
|
if last_idx & 1:
|
||||||
|
rev = True
|
||||||
|
else:
|
||||||
|
rev = False
|
||||||
|
for i in range(3):
|
||||||
|
vert_pool.loop_out(writebuffunc, loop)
|
||||||
|
last_loop = loop
|
||||||
|
last_idx += 1
|
||||||
|
if rev:
|
||||||
|
loop = loop.link_loop_prev
|
||||||
|
else:
|
||||||
|
loop = loop.link_loop_next
|
||||||
|
return last_loop, last_idx
|
||||||
|
|
||||||
|
loop = find_loop_opposite_from_other_face(prim_faces[0], prim_faces[1])
|
||||||
|
if last_loop:
|
||||||
|
vert_pool.loop_out(writebuffunc, loop)
|
||||||
|
last_idx += 1
|
||||||
|
if last_idx & 1:
|
||||||
|
rev = True
|
||||||
|
else:
|
||||||
|
rev = False
|
||||||
|
for i in range(3):
|
||||||
|
vert_pool.loop_out(writebuffunc, loop)
|
||||||
|
last_loop = loop
|
||||||
|
last_idx += 1
|
||||||
|
if rev:
|
||||||
|
loop = loop.link_loop_prev
|
||||||
|
else:
|
||||||
|
loop = loop.link_loop_next
|
||||||
|
|
||||||
|
for i in range(1, len(prim_faces)):
|
||||||
|
loop = find_loop_opposite_from_other_face(prim_faces[i], prim_faces[i-1])
|
||||||
|
vert_pool.loop_out(writebuffunc, loop)
|
||||||
|
last_loop = loop
|
||||||
|
last_idx += 1
|
||||||
|
|
||||||
|
return last_loop, last_idx
|
||||||
|
|
||||||
|
|
||||||
|
def write_out_surface(writebuffunc, vert_pool, bm, island_faces, mat_idx):
|
||||||
|
|
||||||
|
# Centroid of surface
|
||||||
|
centroid = Vector()
|
||||||
|
for f in island_faces:
|
||||||
|
centroid += f.calc_center_bounds()
|
||||||
|
centroid /= len(island_faces)
|
||||||
|
writebuffunc(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
|
||||||
|
|
||||||
|
# Material index
|
||||||
|
writebuffunc(struct.pack('I', mat_idx))
|
||||||
|
|
||||||
|
# AABB of surface
|
||||||
|
aabb_min = Vector((9999999, 9999999, 9999999))
|
||||||
|
aabb_max = Vector((-9999999, -9999999, -9999999))
|
||||||
|
for f in island_faces:
|
||||||
|
for v in f.verts:
|
||||||
|
for c in range(3):
|
||||||
|
if v.co[c] < aabb_min[c]:
|
||||||
|
aabb_min[c] = v.co[c]
|
||||||
|
if v.co[c] > aabb_max[c]:
|
||||||
|
aabb_max[c] = v.co[c]
|
||||||
|
writebuffunc(struct.pack('fff', aabb_min[0], aabb_min[1], aabb_min[2]))
|
||||||
|
writebuffunc(struct.pack('fff', aabb_max[0], aabb_max[1], aabb_max[2]))
|
||||||
|
|
||||||
|
# Average normal of surface
|
||||||
|
avg_norm = Vector()
|
||||||
|
for f in island_faces:
|
||||||
|
avg_norm += f.normal
|
||||||
|
avg_norm.normalize()
|
||||||
|
writebuffunc(struct.pack('fff', avg_norm[0], avg_norm[1], avg_norm[2]))
|
||||||
|
|
||||||
|
# Verts themselves
|
||||||
|
last_loop = None
|
||||||
|
last_idx = 0
|
||||||
|
while len(island_faces):
|
||||||
|
sel_lists_local = []
|
||||||
|
for start_face in island_faces:
|
||||||
|
for e in start_face.edges:
|
||||||
|
next_edges = []
|
||||||
|
for f in e.link_faces:
|
||||||
|
if f == start_face:
|
||||||
|
continue
|
||||||
|
for eg in f.edges:
|
||||||
|
if eg == e:
|
||||||
|
continue
|
||||||
|
next_edges.append(eg)
|
||||||
|
break
|
||||||
|
if len(next_edges) == 0:
|
||||||
|
next_edges.append(None)
|
||||||
|
for e2 in next_edges:
|
||||||
|
island_local = list(island_faces)
|
||||||
|
sel_list = []
|
||||||
|
recursive_faces_strip(sel_list, island_local, start_face, e, e2, None, None)
|
||||||
|
sel_lists_local.append(sel_list)
|
||||||
|
max_count = 0
|
||||||
|
max_sl = None
|
||||||
|
for sl in sel_lists_local:
|
||||||
|
if len(sl) > max_count:
|
||||||
|
max_count = len(sl)
|
||||||
|
max_sl = sl
|
||||||
|
for f in max_sl:
|
||||||
|
island_faces.remove(f)
|
||||||
|
last_loop, last_idx = stripify_primitive(writebuffunc, vert_pool, max_sl, last_loop, last_idx)
|
||||||
|
writebuffunc(struct.pack('B', 0))
|
||||||
|
|
||||||
# Allows restart if initial polygon was not added
|
|
||||||
good = False
|
|
||||||
while not good:
|
|
||||||
|
|
||||||
# Begin a tri-strip primitive (array of vert indices)
|
|
||||||
tri_strip = []
|
|
||||||
|
|
||||||
# Temporary references to trace out strips of triangles
|
|
||||||
temp_poly = poly
|
|
||||||
|
|
||||||
# Rolling references of last two emitted loop-vert sets (b is older)
|
|
||||||
last_loop_vert_a = None
|
|
||||||
last_loop_vert_b = None
|
|
||||||
|
|
||||||
# In the event of vertex-buffer overflow, this will be made true;
|
|
||||||
# resulting in the immediate splitting of a tri-strip
|
|
||||||
is_new_collection = False
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# As long as there is a connected polygon to visit
|
|
||||||
while temp_poly:
|
|
||||||
if 0 == len(tri_strip): # First triangle in strip
|
|
||||||
|
|
||||||
# Order the loops so the last two connect to a next polygon
|
|
||||||
idx0 = mesh.loops[temp_poly.loop_indices[0]].vertex_index
|
|
||||||
idx1 = mesh.loops[temp_poly.loop_indices[1]].vertex_index
|
|
||||||
idx2 = mesh.loops[temp_poly.loop_indices[2]].vertex_index
|
|
||||||
if not _find_polygon_opposite_idxs(mesh, temp_poly, idx1, idx2):
|
|
||||||
loop_idxs = [temp_poly.loop_indices[2], temp_poly.loop_indices[0], temp_poly.loop_indices[1]]
|
|
||||||
if not _find_polygon_opposite_idxs(mesh, temp_poly, idx0, idx1):
|
|
||||||
loop_idxs = [temp_poly.loop_indices[1], temp_poly.loop_indices[2], temp_poly.loop_indices[0]]
|
|
||||||
else:
|
|
||||||
loop_idxs = temp_poly.loop_indices
|
|
||||||
|
|
||||||
# Add three loop-vert vertices to tri-strip
|
|
||||||
for poly_loop_idx in loop_idxs:
|
|
||||||
poly_loop = mesh.loops[poly_loop_idx]
|
|
||||||
loop_vert = _get_loop_set(loop_vert_array[poly_loop.vertex_index], mesh, poly_loop)
|
|
||||||
|
|
||||||
# If rigging, ensure that necessary bones are available and get weights
|
|
||||||
weights = None
|
|
||||||
if rigger:
|
|
||||||
weights = rigger.augment_bone_array_with_lv(mesh, tri_strip_bones, loop_vert)
|
|
||||||
if weights is None:
|
|
||||||
tri_strip_bones_overflow = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if loop_vert not in best_collection['vertices']:
|
|
||||||
best_collection, is_new_collection = self._check_collection_overflow(mesh, best_collection, rigger, uv_count)
|
|
||||||
if is_new_collection:
|
|
||||||
break
|
|
||||||
best_collection['vertices'].append(loop_vert)
|
|
||||||
best_collection['vert_weights'].append(weights)
|
|
||||||
tri_strip.append(best_collection['vertices'].index(loop_vert))
|
|
||||||
last_loop_vert_b = last_loop_vert_a
|
|
||||||
last_loop_vert_a = loop_vert
|
|
||||||
opt_gpu_vert_count += 1
|
|
||||||
#print('appended initial loop', loop_vert[0].loop.index)
|
|
||||||
|
|
||||||
if is_new_collection or tri_strip_bones_overflow:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
else: # Not the first triangle in strip; look up all three loop-verts,
|
|
||||||
# ensure it matches last-2 rolling reference, emit remaining loop-vert
|
|
||||||
|
|
||||||
# Iterate loop verts
|
|
||||||
odd_loop_vert_out = None
|
|
||||||
loop_vert_match_count = 0
|
|
||||||
for poly_loop_idx in temp_poly.loop_indices:
|
|
||||||
poly_loop = mesh.loops[poly_loop_idx]
|
|
||||||
loop_vert = _get_loop_set(loop_vert_array[poly_loop.vertex_index], mesh, poly_loop)
|
|
||||||
|
|
||||||
if (loop_vert == last_loop_vert_a or loop_vert == last_loop_vert_b):
|
|
||||||
loop_vert_match_count += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
odd_loop_vert_out = loop_vert
|
|
||||||
|
|
||||||
|
|
||||||
# Ensure there are two existing matches to continue tri-strip
|
|
||||||
if loop_vert_match_count != 2 or not odd_loop_vert_out:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
# If rigging, ensure that necessary bones are available and get weights
|
|
||||||
weights = None
|
|
||||||
if rigger:
|
|
||||||
weights = rigger.augment_bone_array_with_lv(mesh, tri_strip_bones, odd_loop_vert_out)
|
|
||||||
if weights is None:
|
|
||||||
tri_strip_bones_overflow = True
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
# Add to tri-strip
|
|
||||||
if odd_loop_vert_out not in best_collection['vertices']:
|
|
||||||
best_collection, is_new_collection = self._check_collection_overflow(mesh, best_collection, rigger, uv_count)
|
|
||||||
if is_new_collection:
|
|
||||||
break
|
|
||||||
best_collection['vertices'].append(odd_loop_vert_out)
|
|
||||||
best_collection['vert_weights'].append(weights)
|
|
||||||
tri_strip.append(best_collection['vertices'].index(odd_loop_vert_out))
|
|
||||||
last_loop_vert_b = last_loop_vert_a
|
|
||||||
last_loop_vert_a = odd_loop_vert_out
|
|
||||||
opt_gpu_vert_count += 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# This polygon is good
|
|
||||||
visited_polys.add(temp_poly)
|
|
||||||
|
|
||||||
|
|
||||||
# Find a polygon directly connected to this one to continue strip
|
|
||||||
temp_poly = _find_polygon_opposite_lvs(mesh, temp_poly, last_loop_vert_a, last_loop_vert_b)
|
|
||||||
if temp_poly in visited_polys:
|
|
||||||
temp_poly = None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Add tri-strip to element array
|
|
||||||
if len(tri_strip):
|
|
||||||
best_collection['tri_strips'].append({'mesh':mesh, 'strip':tri_strip, 'strip_bones':tri_strip_bones})
|
|
||||||
good = True
|
|
||||||
if tri_strip_bones_overflow:
|
|
||||||
tri_strip_bones = []
|
|
||||||
tri_strip_bones_overflow = False
|
|
||||||
|
|
||||||
print("Mesh contains", len(mesh.polygons), "triangles")
|
|
||||||
print("Vert count: (%d -> %d)\n" % (len(mesh.loops), opt_gpu_vert_count))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Generate binary vertex buffer of collection index
|
|
||||||
def generate_vertex_buffer(self, index, endian_char):
|
|
||||||
collection = self.collections[index]
|
|
||||||
if not collection:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Positions output
|
|
||||||
pos_out = []
|
|
||||||
|
|
||||||
# Generate vert buffer struct
|
|
||||||
vstruct = struct.Struct(endian_char + 'f')
|
|
||||||
|
|
||||||
# If rigging, determine maximum number of bones in this collection
|
|
||||||
max_bones = 0
|
|
||||||
for i in range(len(collection['vertices'])):
|
|
||||||
weight_count = 0
|
|
||||||
if collection['vert_weights'][i]:
|
|
||||||
weight_count = len(collection['vert_weights'][i])
|
|
||||||
if weight_count > max_bones:
|
|
||||||
max_bones = weight_count
|
|
||||||
max_bones = ROUND_UP_4(max_bones)
|
|
||||||
|
|
||||||
# Build byte array
|
|
||||||
vert_bytes = bytearray()
|
|
||||||
for i in range(len(collection['vertices'])):
|
|
||||||
loop_vert = collection['vertices'][i]
|
|
||||||
bloop = loop_vert[0]
|
|
||||||
mesh = bloop.mesh
|
|
||||||
bvert = mesh.vertices[bloop.loop.vertex_index]
|
|
||||||
#print(bvert.co)
|
|
||||||
|
|
||||||
# Position
|
|
||||||
pos_out.append((bvert.co, bvert.normal))
|
|
||||||
for comp in range(4):
|
|
||||||
if comp in range(len(bvert.co)):
|
|
||||||
vert_bytes += vstruct.pack(bvert.co[comp])
|
|
||||||
else:
|
|
||||||
vert_bytes += vstruct.pack(0.0)
|
|
||||||
|
|
||||||
# Normal
|
|
||||||
for comp in range(4):
|
|
||||||
if comp in range(len(bvert.normal)):
|
|
||||||
vert_bytes += vstruct.pack(bvert.normal[comp])
|
|
||||||
else:
|
|
||||||
vert_bytes += vstruct.pack(0.0)
|
|
||||||
|
|
||||||
# Weights
|
|
||||||
weights = collection['vert_weights'][i]
|
|
||||||
for j in range(max_bones):
|
|
||||||
if j < len(weights):
|
|
||||||
vert_bytes += vstruct.pack(weights[j])
|
|
||||||
else:
|
|
||||||
vert_bytes += vstruct.pack(0.0)
|
|
||||||
|
|
||||||
# UVs
|
|
||||||
added_uvs = 0
|
|
||||||
for uv_idx in range(collection['uv_count']):
|
|
||||||
coords = mesh.uv_layers[uv_idx].data[bloop.loop.index].uv
|
|
||||||
vert_bytes += vstruct.pack(coords[0])
|
|
||||||
vert_bytes += vstruct.pack(-coords[1])
|
|
||||||
added_uvs += 1
|
|
||||||
|
|
||||||
# Pad to 16-byte alignment
|
|
||||||
if added_uvs & 1:
|
|
||||||
vert_bytes += vstruct.pack(0.0)
|
|
||||||
vert_bytes += vstruct.pack(0.0)
|
|
||||||
|
|
||||||
|
|
||||||
return collection['uv_count'], max_bones, vert_bytes, pos_out
|
|
||||||
|
|
||||||
|
|
||||||
# Generate binary element buffer of collection index
|
|
||||||
def generate_element_buffer(self, index, endian_char):
|
|
||||||
collection = self.collections[index]
|
|
||||||
if not collection:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Numeric array out
|
|
||||||
arr_out = []
|
|
||||||
|
|
||||||
# Generate element buffer struct
|
|
||||||
estruct = struct.Struct(endian_char + 'H')
|
|
||||||
|
|
||||||
# Build mesh-primitive hierarchy
|
|
||||||
last_mesh = collection['tri_strips'][0]['mesh']
|
|
||||||
mesh_primitives = {'mesh':last_mesh, 'primitives':[]}
|
|
||||||
collection_primitives = [mesh_primitives]
|
|
||||||
|
|
||||||
# Collection element byte-array
|
|
||||||
cur_offset = 0
|
|
||||||
element_bytes = bytearray()
|
|
||||||
|
|
||||||
# Last element index entry and strip length for forming degenerate strip
|
|
||||||
last_elem = None
|
|
||||||
strip_len = 0
|
|
||||||
|
|
||||||
# Last strip bone array (for rigging)
|
|
||||||
last_strip_bones = collection['tri_strips'][0]['strip_bones']
|
|
||||||
|
|
||||||
# Build single degenerate tri-strip
|
|
||||||
for strip in collection['tri_strips']:
|
|
||||||
#print('new strip', collection['tri_strips'].index(strip))
|
|
||||||
|
|
||||||
if last_mesh != strip['mesh'] or last_strip_bones != strip['strip_bones']:
|
|
||||||
#print('splitting primitive')
|
|
||||||
# New mesh; force new strip
|
|
||||||
mesh_primitives['primitives'].append({'offset':cur_offset, 'length':strip_len, 'bones':last_strip_bones})
|
|
||||||
cur_offset += strip_len
|
|
||||||
last_elem = None
|
|
||||||
strip_len = 0
|
|
||||||
last_mesh = strip['mesh']
|
|
||||||
mesh_primitives = {'mesh':last_mesh, 'primitives':[]}
|
|
||||||
collection_primitives.append(mesh_primitives)
|
|
||||||
|
|
||||||
elif last_elem is not None:
|
|
||||||
#print('extending primitive')
|
|
||||||
# Existing mesh being extended as degenerate strip
|
|
||||||
strip_len += 2
|
|
||||||
element_bytes += estruct.pack(last_elem)
|
|
||||||
element_bytes += estruct.pack(strip['strip'][0])
|
|
||||||
arr_out.append(last_elem)
|
|
||||||
arr_out.append(strip['strip'][0])
|
|
||||||
|
|
||||||
# If current element count is odd, add additional degenerate strip to make it even
|
|
||||||
# This ensures that the sub-strip has proper winding-order for backface culling
|
|
||||||
if (strip_len & 1):
|
|
||||||
strip_len += 1
|
|
||||||
element_bytes += estruct.pack(strip['strip'][0])
|
|
||||||
arr_out.append(strip['strip'][0])
|
|
||||||
|
|
||||||
|
|
||||||
# Primitive tri-strip byte array
|
|
||||||
for idx in strip['strip']:
|
|
||||||
#print(idx)
|
|
||||||
strip_len += 1
|
|
||||||
element_bytes += estruct.pack(idx)
|
|
||||||
arr_out.append(idx)
|
|
||||||
last_elem = idx
|
|
||||||
|
|
||||||
# Final mesh entry
|
|
||||||
mesh_primitives['primitives'].append({'offset':cur_offset, 'length':strip_len, 'bones':last_strip_bones})
|
|
||||||
cur_offset += strip_len
|
|
||||||
|
|
||||||
return collection_primitives, element_bytes, arr_out
|
|
||||||
|
|
||||||
|
|
||||||
# Generate binary draw-index buffer of collection index
|
|
||||||
def generate_index_buffer(self, collection_primitives, endian_char, rigger):
|
|
||||||
|
|
||||||
# Bytearray to fill
|
|
||||||
index_bytes = bytearray()
|
|
||||||
|
|
||||||
# Submesh count
|
|
||||||
index_bytes += struct.pack(endian_char + 'I', len(collection_primitives))
|
|
||||||
|
|
||||||
# And array
|
|
||||||
for mesh in collection_primitives:
|
|
||||||
|
|
||||||
# Primitive count
|
|
||||||
index_bytes += struct.pack(endian_char + 'I', len(mesh['primitives']))
|
|
||||||
|
|
||||||
# Primitive array
|
|
||||||
for prim in mesh['primitives']:
|
|
||||||
|
|
||||||
# If rigging, append skin index
|
|
||||||
if rigger:
|
|
||||||
skin_index = rigger.augment_skin(prim['bones'])
|
|
||||||
index_bytes += struct.pack(endian_char + 'I', skin_index)
|
|
||||||
|
|
||||||
index_bytes += struct.pack(endian_char + 'I', 2)
|
|
||||||
index_bytes += struct.pack(endian_char + 'I', prim['offset'])
|
|
||||||
index_bytes += struct.pack(endian_char + 'I', prim['length'])
|
|
||||||
|
|
||||||
return index_bytes
|
|
||||||
|
|
||||||
|
|
||||||
# C-generation operator
|
|
||||||
import bmesh
|
|
||||||
class hmdl_mesh_operator(bpy.types.Operator):
|
|
||||||
bl_idname = "scene.hmdl_mesh"
|
|
||||||
bl_label = "HMDL C mesh maker"
|
|
||||||
bl_description = "HMDL Mesh source generation utility"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return context.object and context.object.type == 'MESH'
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
copy_mesh = context.object.data.copy()
|
|
||||||
copy_obj = context.object.copy()
|
|
||||||
copy_obj.data = copy_mesh
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.from_mesh(copy_mesh)
|
|
||||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
|
||||||
#to_remove = []
|
|
||||||
#for face in bm.faces:
|
|
||||||
# if face.material_index != 7:
|
|
||||||
# to_remove.append(face)
|
|
||||||
#bmesh.ops.delete(bm, geom=to_remove, context=5)
|
|
||||||
bm.to_mesh(copy_mesh)
|
|
||||||
bm.free()
|
|
||||||
|
|
||||||
context.scene.objects.link(copy_obj)
|
|
||||||
rmesh = hmdl_mesh()
|
|
||||||
rmesh.add_mesh(copy_mesh, None, 0)
|
|
||||||
|
|
||||||
str_out = '/* Vertex Buffer */\nstatic const float VERT_BUF[] = {\n'
|
|
||||||
vert_arr = rmesh.generate_vertex_buffer(0, '<')[3]
|
|
||||||
for v in vert_arr:
|
|
||||||
str_out += ' %f, %f, %f, 0.0, %f, %f, %f, 0.0,\n' % (v[0][0], v[0][1], v[0][2], v[1][0], v[1][1], v[1][2])
|
|
||||||
ebuf_arr = rmesh.generate_element_buffer(0, '<')[2]
|
|
||||||
str_out += '};\n\n/* Element Buffer */\n#define ELEM_BUF_COUNT %d\nstatic const u16 ELEM_BUF[] = {\n' % len(ebuf_arr)
|
|
||||||
for e in ebuf_arr:
|
|
||||||
str_out += ' %d,\n' % e
|
|
||||||
str_out += '};\n'
|
|
||||||
|
|
||||||
context.scene.objects.unlink(copy_obj)
|
|
||||||
bpy.data.objects.remove(copy_obj)
|
|
||||||
bpy.data.meshes.remove(copy_mesh)
|
|
||||||
|
|
||||||
context.window_manager.clipboard = str_out
|
|
||||||
self.report({'INFO'}, "Wrote mesh C to clipboard")
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
|
@ -6,12 +6,33 @@ Traces the 'Blender Internal' shader node structure to generate a
|
||||||
HECL combiner string
|
HECL combiner string
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import bpy, bpy.path, os.path
|
||||||
|
|
||||||
|
def get_texmap_idx(tex_list, name):
|
||||||
|
for i in range(len(tex_list)):
|
||||||
|
if tex_list[i] == name:
|
||||||
|
return i
|
||||||
|
retval = len(tex_list)
|
||||||
|
tex_list.append(name)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def get_texture_path(name):
|
||||||
|
if name not in bpy.data.textures:
|
||||||
|
raise RuntimeError('unable to find %s texture' % name)
|
||||||
|
tex = bpy.data.textures[name]
|
||||||
|
if tex.type != 'IMAGE':
|
||||||
|
raise RuntimeError('%s texture unsupported for %s, please save as IMAGE' % (tex.type, name))
|
||||||
|
img = tex.image
|
||||||
|
if not img:
|
||||||
|
raise RuntimeError('image not set in %s' % name)
|
||||||
|
return os.path.normpath(bpy.path.abspath(img.filepath))
|
||||||
|
|
||||||
# Trace color node structure
|
# Trace color node structure
|
||||||
def recursive_color_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
def recursive_color_trace(mat_obj, mesh_obj, tex_list, node, socket=None):
|
||||||
|
|
||||||
if node.type == 'OUTPUT':
|
if node.type == 'OUTPUT':
|
||||||
if node.inputs['Color'].is_linked:
|
if node.inputs['Color'].is_linked:
|
||||||
return recursive_color_trace(mat_obj, mesh_obj, blend_path, node.inputs['Color'].links[0].from_node, node.inputs['Color'].links[0].from_socket)
|
return recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs['Color'].links[0].from_node, node.inputs['Color'].links[0].from_socket)
|
||||||
else:
|
else:
|
||||||
return 'vec3(%f, %f, %f)' % (node.inputs['Color'].default_value[0],
|
return 'vec3(%f, %f, %f)' % (node.inputs['Color'].default_value[0],
|
||||||
node.inputs['Color'].default_value[1],
|
node.inputs['Color'].default_value[1],
|
||||||
|
@ -20,14 +41,14 @@ def recursive_color_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
elif node.type == 'MIX_RGB':
|
elif node.type == 'MIX_RGB':
|
||||||
|
|
||||||
if node.inputs[1].is_linked:
|
if node.inputs[1].is_linked:
|
||||||
a_input = recursive_color_trace(mat_obj, mesh_obj, blend_path, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
|
a_input = recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
|
||||||
else:
|
else:
|
||||||
a_input = 'vec3(%f, %f, %f)' % (node.inputs[1].default_value[0],
|
a_input = 'vec3(%f, %f, %f)' % (node.inputs[1].default_value[0],
|
||||||
node.inputs[1].default_value[1],
|
node.inputs[1].default_value[1],
|
||||||
node.inputs[1].default_value[2])
|
node.inputs[1].default_value[2])
|
||||||
|
|
||||||
if node.inputs[2].is_linked:
|
if node.inputs[2].is_linked:
|
||||||
b_input = recursive_color_trace(mat_obj, mesh_obj, blend_path, node.inputs[2].links[0].from_node, node.inputs[2].links[0].from_socket)
|
b_input = recursive_color_trace(mat_obj, mesh_obj, tex_list, node.inputs[2].links[0].from_node, node.inputs[2].links[0].from_socket)
|
||||||
else:
|
else:
|
||||||
b_input = 'vec3(%f, %f, %f)' % (node.inputs[2].default_value[0],
|
b_input = 'vec3(%f, %f, %f)' % (node.inputs[2].default_value[0],
|
||||||
node.inputs[2].default_value[1],
|
node.inputs[2].default_value[1],
|
||||||
|
@ -53,7 +74,7 @@ def recursive_color_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
soc_from = node.inputs['Vector'].links[0].from_socket
|
soc_from = node.inputs['Vector'].links[0].from_socket
|
||||||
|
|
||||||
if soc_from.node.type == 'GROUP':
|
if soc_from.node.type == 'GROUP':
|
||||||
matrix_str = '%s(' % soc_from.node.node_tree.name
|
matrix_str = '%s(%%s, ' % soc_from.node.node_tree.name
|
||||||
for s in range(len(soc_from.node.inputs)-1):
|
for s in range(len(soc_from.node.inputs)-1):
|
||||||
soc = soc_from.node.inputs[s+1]
|
soc = soc_from.node.inputs[s+1]
|
||||||
if len(soc.links):
|
if len(soc.links):
|
||||||
|
@ -87,42 +108,58 @@ def recursive_color_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
# Resolve map and matrix index
|
# Resolve map and matrix index
|
||||||
node_label = soc_from.node.label
|
node_label = soc_from.node.label
|
||||||
if not matrix_str and node_label.startswith('MTX_'):
|
if not matrix_str and node_label.startswith('MTX_'):
|
||||||
matrix_str = 'hecl_TexMtx[%d]' % int(node_label[4:])
|
matrix_str = 'HECLTexMtx(%%s, %d)' % int(node_label[4:])
|
||||||
|
|
||||||
if soc_from.name == 'UV':
|
if soc_from.name == 'UV':
|
||||||
uv_name = soc_from.node.uv_layer
|
uv_name = soc_from.node.uv_layer
|
||||||
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
||||||
if uv_idx == -1:
|
if uv_idx == -1:
|
||||||
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
||||||
uvsource_str = 'hecl_TexCoord[%d]' % uv_idx
|
uvsource_str = 'HECLUV(%d)' % uv_idx
|
||||||
|
|
||||||
elif soc_from.name == 'Normal':
|
elif soc_from.name == 'Normal':
|
||||||
uvsource_str = 'hecl_TexCoordModelViewNormal'
|
uvsource_str = 'HECLNormal()'
|
||||||
|
|
||||||
elif soc_from.name == 'View':
|
elif soc_from.name == 'View':
|
||||||
uvsource_str = 'hecl_TexCoordModelViewPosition'
|
uvsource_str = 'HECLView()'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
|
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
|
||||||
|
|
||||||
if socket.name == 'Value':
|
if socket.name == 'Value':
|
||||||
if matrix_str:
|
if matrix_str:
|
||||||
return 'texture("%s:%s", %s, %s).a' % (blend_path, node.texture.name, uvsource_str, matrix_str)
|
uvsource_str = matrix_str % uvsource_str
|
||||||
else:
|
return 'texture(%d, %s).a' % (get_texmap_idx(tex_list, node.texture.name), uvsource_str)
|
||||||
return 'texture("%s:%s", %s).a' % (blend_path, node.texture.name, uvsource_str)
|
|
||||||
if socket.name == 'Color':
|
if socket.name == 'Color':
|
||||||
if matrix_str:
|
if matrix_str:
|
||||||
return 'texture("%s:%s", %s, %s)' % (blend_path, node.texture.name, uvsource_str, matrix_str)
|
uvsource_str = matrix_str % uvsource_str
|
||||||
else:
|
return 'texture(%d, %s)' % (get_texmap_idx(tex_list, node.texture.name), uvsource_str)
|
||||||
return 'texture("%s:%s", %s)' % (blend_path, node.texture.name, uvsource_str)
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Only the 'Value' or 'Color' output sockets may be used from Texture nodes")
|
raise RuntimeError("Only the 'Value' or 'Color' output sockets may be used from Texture nodes")
|
||||||
|
|
||||||
|
elif node.type == 'GROUP':
|
||||||
|
|
||||||
|
group_str = '%s(' % node.node_tree.name
|
||||||
|
did_first = False
|
||||||
|
for input in node.inputs:
|
||||||
|
if input.type == 'RGBA':
|
||||||
|
if did_first:
|
||||||
|
group_str += ', '
|
||||||
|
if input.is_linked:
|
||||||
|
group_str += recursive_color_trace(mat_obj, mesh_obj, tex_list, input.links[0].from_node, input.links[0].from_socket)
|
||||||
|
else:
|
||||||
|
group_str += 'vec3(%f, %f, %f)' % (input.default_value[0],
|
||||||
|
input.default_value[1],
|
||||||
|
input.default_value[2])
|
||||||
|
did_first = True
|
||||||
|
group_str += ')'
|
||||||
|
return group_str
|
||||||
|
|
||||||
elif node.type == 'RGB':
|
elif node.type == 'RGB':
|
||||||
|
|
||||||
if node.label.startswith('DYNAMIC_'):
|
if node.label.startswith('DYNAMIC_'):
|
||||||
dynamic_index = int(node.label[8:])
|
dynamic_index = int(node.label[8:])
|
||||||
return 'hecl_KColor[%d]' % dynamic_index
|
return 'HECLColorReg(%d)' % dynamic_index
|
||||||
|
|
||||||
return '%f' % node.outputs['Color'].default_value
|
return '%f' % node.outputs['Color'].default_value
|
||||||
|
|
||||||
|
@ -131,7 +168,7 @@ def recursive_color_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
if mat_obj.use_shadeless:
|
if mat_obj.use_shadeless:
|
||||||
return 'vec3(1.0)'
|
return 'vec3(1.0)'
|
||||||
else:
|
else:
|
||||||
return 'hecl_Lighting'
|
return 'HECLLighting()'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("HMDL is unable to process '{0}' shader nodes in '{1}'".format(node.type, mat_obj.name))
|
raise RuntimeError("HMDL is unable to process '{0}' shader nodes in '{1}'".format(node.type, mat_obj.name))
|
||||||
|
@ -139,23 +176,23 @@ def recursive_color_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
|
|
||||||
|
|
||||||
# Trace alpha node structure
|
# Trace alpha node structure
|
||||||
def recursive_alpha_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
def recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node, socket=None):
|
||||||
|
|
||||||
if node.type == 'OUTPUT':
|
if node.type == 'OUTPUT':
|
||||||
if node.inputs['Alpha'].is_linked:
|
if node.inputs['Alpha'].is_linked:
|
||||||
return recursive_alpha_trace(mat_obj, mesh_obj, blend_path, node.inputs['Alpha'].links[0].from_node, node.inputs['Alpha'].links[0].from_socket)
|
return recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs['Alpha'].links[0].from_node, node.inputs['Alpha'].links[0].from_socket)
|
||||||
else:
|
else:
|
||||||
return '%f' % node.inputs['Alpha'].default_value
|
return '%f' % node.inputs['Alpha'].default_value
|
||||||
|
|
||||||
elif node.type == 'MATH':
|
elif node.type == 'MATH':
|
||||||
|
|
||||||
if node.inputs[0].is_linked:
|
if node.inputs[0].is_linked:
|
||||||
a_input = recursive_alpha_trace(mat_obj, mesh_obj, blend_path, node.inputs[0].links[0].from_node, node.inputs[0].links[0].from_socket)
|
a_input = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs[0].links[0].from_node, node.inputs[0].links[0].from_socket)
|
||||||
else:
|
else:
|
||||||
a_input = '%f' % node.inputs[0].default_value
|
a_input = '%f' % node.inputs[0].default_value
|
||||||
|
|
||||||
if node.inputs[1].is_linked:
|
if node.inputs[1].is_linked:
|
||||||
b_input = recursive_alpha_trace(plat, mat_obj, mesh_obj, tex_list, mtx_dict, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
|
b_input = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, node.inputs[1].links[0].from_node, node.inputs[1].links[0].from_socket)
|
||||||
else:
|
else:
|
||||||
b_input = '%f' % node.inputs[1].default_value
|
b_input = '%f' % node.inputs[1].default_value
|
||||||
|
|
||||||
|
@ -179,7 +216,7 @@ def recursive_alpha_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
soc_from = node.inputs['Vector'].links[0].from_socket
|
soc_from = node.inputs['Vector'].links[0].from_socket
|
||||||
|
|
||||||
if soc_from.node.type == 'GROUP':
|
if soc_from.node.type == 'GROUP':
|
||||||
matrix_str = '%s(' % soc_from.node.node_tree.name
|
matrix_str = '%s(%%s, ' % soc_from.node.node_tree.name
|
||||||
for s in range(len(soc_from.node.inputs)-1):
|
for s in range(len(soc_from.node.inputs)-1):
|
||||||
soc = soc_from.node.inputs[s+1]
|
soc = soc_from.node.inputs[s+1]
|
||||||
if len(soc.links):
|
if len(soc.links):
|
||||||
|
@ -213,37 +250,52 @@ def recursive_alpha_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
# Resolve map and matrix index
|
# Resolve map and matrix index
|
||||||
node_label = soc_from.node.label
|
node_label = soc_from.node.label
|
||||||
if not matrix_str and node_label.startswith('MTX_'):
|
if not matrix_str and node_label.startswith('MTX_'):
|
||||||
matrix_str = 'hecl_TexMtx[%d]' % int(node_label[4:])
|
matrix_str = 'HECLTexMtx(%%s, %d)' % int(node_label[4:])
|
||||||
|
|
||||||
if soc_from.name == 'UV':
|
if soc_from.name == 'UV':
|
||||||
uv_name = soc_from.node.uv_layer
|
uv_name = soc_from.node.uv_layer
|
||||||
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
|
||||||
if uv_idx == -1:
|
if uv_idx == -1:
|
||||||
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
|
||||||
uvsource_str = 'hecl_TexCoord[%d]' % uv_idx
|
uvsource_str = 'HECLUV(%d)' % uv_idx
|
||||||
|
|
||||||
elif soc_from.name == 'Normal':
|
elif soc_from.name == 'Normal':
|
||||||
uvsource_str = 'hecl_TexCoordModelViewNormal'
|
uvsource_str = 'HECLNormal()'
|
||||||
|
|
||||||
elif soc_from.name == 'View':
|
elif soc_from.name == 'View':
|
||||||
uvsource_str = 'hecl_TexCoordModelViewPosition'
|
uvsource_str = 'HECLView()'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
|
raise RuntimeError("Only the 'UV', 'Normal' and 'View' sockets may be used from 'Geometry' nodes")
|
||||||
|
|
||||||
if socket.name == 'Value':
|
if socket.name == 'Value':
|
||||||
if matrix_str:
|
if matrix_str:
|
||||||
return 'texture("%s:%s", %s, %s).a' % (blend_path, node.texture.name, uvsource_str, matrix_str)
|
uvsource_str = matrix_str % uvsource_str
|
||||||
else:
|
return 'texture(%d, %s).a' % (get_texmap_idx(tex_list, node.texture.name), uvsource_str)
|
||||||
return 'texture("%s:%s", %s).a' % (blend_path, node.texture.name, uvsource_str)
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Only the 'Value' output sockets may be used from Texture nodes")
|
raise RuntimeError("Only the 'Value' output sockets may be used from Texture nodes")
|
||||||
|
|
||||||
|
elif node.type == 'GROUP':
|
||||||
|
|
||||||
|
group_str = '%s(' % node.node_tree.name
|
||||||
|
did_first = False
|
||||||
|
for input in node.inputs:
|
||||||
|
if input.type == 'VALUE':
|
||||||
|
if did_first:
|
||||||
|
group_str += ', '
|
||||||
|
if input.is_linked:
|
||||||
|
group_str += recursive_alpha_trace(mat_obj, mesh_obj, tex_list, input.links[0].from_node, input.links[0].from_socket)
|
||||||
|
else:
|
||||||
|
group_str += '%f' % input.default_value
|
||||||
|
did_first = True
|
||||||
|
group_str += ')'
|
||||||
|
return group_str
|
||||||
|
|
||||||
elif node.type == 'VALUE':
|
elif node.type == 'VALUE':
|
||||||
|
|
||||||
if node.label.startswith('DYNAMIC_'):
|
if node.label.startswith('DYNAMIC_'):
|
||||||
dynamic_index = int(node.label[8:])
|
dynamic_index = int(node.label[8:])
|
||||||
return 'hecl_KColor[%d].a' % dynamic_index
|
return 'HECLColorReg(%d).a' % dynamic_index
|
||||||
|
|
||||||
return '%f' % node.outputs['Value'].default_value
|
return '%f' % node.outputs['Value'].default_value
|
||||||
|
|
||||||
|
@ -256,7 +308,7 @@ def recursive_alpha_trace(mat_obj, mesh_obj, blend_path, node, socket=None):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def shader(mat_obj, mesh_obj, blend_path):
|
def shader(mat_obj, mesh_obj):
|
||||||
|
|
||||||
if not mat_obj.use_nodes:
|
if not mat_obj.use_nodes:
|
||||||
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
|
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
|
||||||
|
@ -268,25 +320,19 @@ def shader(mat_obj, mesh_obj, blend_path):
|
||||||
output_node = mat_obj.node_tree.nodes['Output']
|
output_node = mat_obj.node_tree.nodes['Output']
|
||||||
|
|
||||||
# Trace nodes and build result
|
# Trace nodes and build result
|
||||||
color_trace_result = recursive_color_trace(mat_obj, mesh_obj, blend_path, output_node)
|
tex_list = []
|
||||||
alpha_trace_result = recursive_alpha_trace(mat_obj, mesh_obj, blend_path, output_node)
|
color_trace_result = recursive_color_trace(mat_obj, mesh_obj, tex_list, output_node)
|
||||||
|
alpha_trace_result = recursive_alpha_trace(mat_obj, mesh_obj, tex_list, output_node)
|
||||||
|
|
||||||
|
# Resolve texture paths
|
||||||
|
tex_paths = [get_texture_path(name) for name in tex_list]
|
||||||
|
|
||||||
blend_src = 'hecl_One'
|
|
||||||
blend_dest = 'hecl_Zero'
|
|
||||||
if mat_obj.game_settings.alpha_blend == 'ALPHA' or mat_obj.game_settings.alpha_blend == 'ALPHA_SORT':
|
if mat_obj.game_settings.alpha_blend == 'ALPHA' or mat_obj.game_settings.alpha_blend == 'ALPHA_SORT':
|
||||||
blend_src = 'hecl_SrcAlpha'
|
return "HECLBlend(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
|
||||||
blend_dest = 'hecl_OneMinusSrcAlpha'
|
|
||||||
elif mat_obj.game_settings.alpha_blend == 'ADD':
|
elif mat_obj.game_settings.alpha_blend == 'ADD':
|
||||||
blend_src = 'hecl_SrcAlpha'
|
return "HECLAdditive(%s, %s)" % (color_trace_result, alpha_trace_result), tex_paths
|
||||||
blend_dest = 'hecl_One'
|
else:
|
||||||
|
return "HECLOpaque(%s)" % color_trace_result, tex_paths
|
||||||
# All done!
|
|
||||||
return '''\
|
|
||||||
hecl_BlendSrcFactor = %s;
|
|
||||||
hecl_BlendDestFactor = %s;
|
|
||||||
hecl_FragColor[0] = %s;
|
|
||||||
hecl_FragColor[0].a = %s;
|
|
||||||
''' % (blend_src, blend_dest, color_trace_result, alpha_trace_result)
|
|
||||||
|
|
||||||
# DEBUG operator
|
# DEBUG operator
|
||||||
import bpy
|
import bpy
|
||||||
|
@ -300,9 +346,11 @@ class hecl_shader_operator(bpy.types.Operator):
|
||||||
return context.object and context.object.type == 'MESH'
|
return context.object and context.object.type == 'MESH'
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
shad = shader(context.object.active_material, context.object, bpy.data.filepath)
|
shad, texs = shader(context.object.active_material, context.object, bpy.data.filepath)
|
||||||
|
|
||||||
vs = bpy.data.texts.new('HECL SHADER')
|
vs = bpy.data.texts.new('HECL SHADER')
|
||||||
vs.write(shad)
|
vs.write((shad + '\n'))
|
||||||
|
for tex in texs:
|
||||||
|
vs.write(tex + '\n')
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
'''
|
|
||||||
HMDL Export Blender Addon
|
|
||||||
By Jack Andersen <jackoalan@gmail.com>
|
|
||||||
|
|
||||||
This file provides the means to generate an RGBA TXTR resource
|
|
||||||
buffer for packaging into an .hlpk (yes, I know this is slow,
|
|
||||||
but it's very flexible and supports Blender's procedural textures)
|
|
||||||
'''
|
|
||||||
|
|
||||||
from mathutils import Vector
|
|
||||||
import struct
|
|
||||||
|
|
||||||
def count_bits(num):
|
|
||||||
accum = 0
|
|
||||||
index = 0
|
|
||||||
for i in range(32):
|
|
||||||
if ((num >> i) & 1):
|
|
||||||
accum += 1
|
|
||||||
index = i
|
|
||||||
return accum, index
|
|
||||||
|
|
||||||
def make_txtr(tex, size=(512,512)):
|
|
||||||
|
|
||||||
if tex.type == 'IMAGE':
|
|
||||||
size = tex.image.size
|
|
||||||
|
|
||||||
# Validate image for mipmapping
|
|
||||||
can_mipmap = False
|
|
||||||
w_bits, w_idx = count_bits(size[0])
|
|
||||||
h_bits, h_idx = count_bits(size[1])
|
|
||||||
if w_bits == 1 and h_bits == 1 and tex.use_mipmap:
|
|
||||||
can_mipmap = True
|
|
||||||
|
|
||||||
# Main image 2D array
|
|
||||||
main_array = []
|
|
||||||
for y in range(size[1]):
|
|
||||||
row = []
|
|
||||||
main_array.append(row)
|
|
||||||
for x in range(size[0]):
|
|
||||||
texel = tex.evaluate((x * 2 / size[0] - 1.0, y * 2 / size[1] - 1.0, 0))
|
|
||||||
row.append(texel)
|
|
||||||
|
|
||||||
# Count potential mipmap levels
|
|
||||||
series_count = 1
|
|
||||||
if can_mipmap:
|
|
||||||
if size[0] > size[1]:
|
|
||||||
series_count = w_idx + 1
|
|
||||||
else:
|
|
||||||
series_count = h_idx + 1
|
|
||||||
|
|
||||||
# Make header
|
|
||||||
tex_bytes = struct.pack('IHHI', 0, size[0], size[1], series_count)
|
|
||||||
|
|
||||||
# Initial mipmap level
|
|
||||||
for y in main_array:
|
|
||||||
for x in y:
|
|
||||||
tex_bytes += struct.pack('BBBB',
|
|
||||||
min(255, int(x[0]*256)),
|
|
||||||
min(255, int(x[1]*256)),
|
|
||||||
min(255, int(x[2]*256)),
|
|
||||||
min(255, int(x[3]*256)))
|
|
||||||
|
|
||||||
# Prepare mipmap maker
|
|
||||||
if can_mipmap:
|
|
||||||
|
|
||||||
# Box filter
|
|
||||||
prev_array = main_array
|
|
||||||
for i in range(series_count - 1):
|
|
||||||
new_array = []
|
|
||||||
for y in range(max(len(prev_array) // 2, 1)):
|
|
||||||
y1 = prev_array[y*2]
|
|
||||||
if len(prev_array) > 1:
|
|
||||||
y2 = prev_array[y*2+1]
|
|
||||||
else:
|
|
||||||
y2 = prev_array[y*2]
|
|
||||||
new_row = []
|
|
||||||
new_array.append(new_row)
|
|
||||||
for x in range(max(len(y1) // 2, 1)):
|
|
||||||
texel_val = Vector((0,0,0,0))
|
|
||||||
texel_val += y1[x*2]
|
|
||||||
texel_val += y2[x*2]
|
|
||||||
if len(y1) > 1:
|
|
||||||
texel_val += y1[x*2+1]
|
|
||||||
texel_val += y2[x*2+1]
|
|
||||||
else:
|
|
||||||
texel_val += y1[x*2]
|
|
||||||
texel_val += y2[x*2]
|
|
||||||
texel_val /= 4
|
|
||||||
new_row.append(texel_val)
|
|
||||||
tex_bytes += struct.pack('BBBB',
|
|
||||||
min(255, int(texel_val[0]*256)),
|
|
||||||
min(255, int(texel_val[1]*256)),
|
|
||||||
min(255, int(texel_val[2]*256)),
|
|
||||||
min(255, int(texel_val[3]*256)))
|
|
||||||
|
|
||||||
prev_array = new_array
|
|
||||||
|
|
||||||
return tex_bytes
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ Positions, Normals, UV coordinates, and Weight Vectors
|
||||||
|
|
||||||
import struct, bpy, bmesh
|
import struct, bpy, bmesh
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from . import HMDLShader, HMDLSkin, HMDLMesh, HMDLTxtr
|
from . import HMDLShader, HMDLSkin, HMDLMesh
|
||||||
|
|
||||||
def get_3d_context(object_):
|
def get_3d_context(object_):
|
||||||
window = bpy.context.window_manager.windows[0]
|
window = bpy.context.window_manager.windows[0]
|
||||||
|
@ -91,22 +91,10 @@ def generate_skeleton_info(armature, endian_char='<'):
|
||||||
# Takes a Blender 'Mesh' object (not the datablock)
|
# Takes a Blender 'Mesh' object (not the datablock)
|
||||||
# and performs a one-shot conversion process to HMDL; packaging
|
# and performs a one-shot conversion process to HMDL; packaging
|
||||||
# into the HECL data-pipeline and returning a hash once complete
|
# into the HECL data-pipeline and returning a hash once complete
|
||||||
def cook(writebuffunc, platform, endianchar):
|
def cook(writebuffunc, mesh_obj, max_skin_banks):
|
||||||
print('COOKING HMDL')
|
|
||||||
return True
|
|
||||||
mesh_obj = bpy.data.objects[bpy.context.scene.hecl_mesh_obj]
|
|
||||||
if mesh_obj.type != 'MESH':
|
if mesh_obj.type != 'MESH':
|
||||||
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
||||||
|
|
||||||
# Partial meshes
|
|
||||||
part_meshes = set()
|
|
||||||
|
|
||||||
# Start with shader, mesh and rigging-info generation.
|
|
||||||
# Use SHA1 hashing to determine what the ID-hash will be when
|
|
||||||
# shaders are packaged; strip out duplicates
|
|
||||||
shader_set = []
|
|
||||||
rigger = None
|
|
||||||
|
|
||||||
# Normalize all vertex weights
|
# Normalize all vertex weights
|
||||||
override = get_3d_context(mesh_obj)
|
override = get_3d_context(mesh_obj)
|
||||||
try:
|
try:
|
||||||
|
@ -122,20 +110,11 @@ def cook(writebuffunc, platform, endianchar):
|
||||||
copy_obj.scale = mesh_obj.scale
|
copy_obj.scale = mesh_obj.scale
|
||||||
bpy.context.scene.objects.link(copy_obj)
|
bpy.context.scene.objects.link(copy_obj)
|
||||||
|
|
||||||
# If skinned, establish rigging generator
|
# Create master triangulated BMesh and VertPool
|
||||||
if len(mesh_obj.vertex_groups):
|
bm_master = bmesh.new()
|
||||||
rigger = hmdl_skin.hmdl_skin(max_bone_count, mesh_obj.vertex_groups)
|
bm_master.from_mesh(copy_obj.data)
|
||||||
|
bmesh.ops.triangulate(bm_master, faces=bm_master.faces)
|
||||||
# Determine count of transformation matricies to deliver to shader set
|
vert_pool = HMDLMesh.VertPool(bm_master)
|
||||||
actual_max_bone_counts = [1] * len(mesh_obj.data.materials)
|
|
||||||
max_bone_count = 1
|
|
||||||
for mat_idx in range(len(mesh_obj.data.materials)):
|
|
||||||
mat = mesh_obj.data.materials[mat_idx]
|
|
||||||
count = hmdl_shader.max_transform_counter(mat, mesh_obj)
|
|
||||||
if count > 1:
|
|
||||||
actual_max_bone_counts[mat_idx] = count
|
|
||||||
if count > max_bone_count:
|
|
||||||
max_bone_count = count
|
|
||||||
|
|
||||||
# Sort materials by pass index first
|
# Sort materials by pass index first
|
||||||
sorted_material_idxs = []
|
sorted_material_idxs = []
|
||||||
|
@ -150,133 +129,78 @@ def cook(writebuffunc, platform, endianchar):
|
||||||
source_mat_set.discard(min_mat_idx)
|
source_mat_set.discard(min_mat_idx)
|
||||||
|
|
||||||
# Generate shaders
|
# Generate shaders
|
||||||
actual_max_texmtx_count = 0
|
if mesh_obj.data.hecl_material_count > 0:
|
||||||
for mat_idx in sorted_material_idxs:
|
writebuffunc(struct.pack('I', len(mesh_obj.data.hecl_material_count)))
|
||||||
|
for grp_idx in range(mesh_obj.data.hecl_material_count):
|
||||||
shader_hashes = []
|
writebuffunc(struct.pack('I', len(sorted_material_idxs)))
|
||||||
shader_uv_count = 0
|
for mat_idx in sorted_material_idxs:
|
||||||
|
found = False
|
||||||
if mesh_obj.data.hecl_material_count > 0:
|
|
||||||
for grp_idx in range(mesh_obj.data.hecl_material_count):
|
|
||||||
for mat in bpy.data.materials:
|
for mat in bpy.data.materials:
|
||||||
if mat.name.endswith('_%u_%u' % (grp_idx, mat_idx)):
|
if mat.name.endswith('_%u_%u' % (grp_idx, mat_idx)):
|
||||||
hecl_str = hmdl_shader.shader(mat, mesh_obj, bpy.data.filepath)
|
hecl_str, texs = hmdl_shader.shader(mat, mesh_obj, bpy.data.filepath)
|
||||||
|
writebuffunc((hecl_str + '\n').encode())
|
||||||
else:
|
writebuffunc(struct.pack('I', len(texs)))
|
||||||
|
for tex in texs:
|
||||||
|
writebuffunc((tex + '\n').encode())
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
raise RuntimeError('uneven material set %d in %s' % (grp_idx, mesh_obj.name))
|
||||||
|
else:
|
||||||
|
writebuffunc(struct.pack('II', 1, len(sorted_material_idxs)))
|
||||||
|
for mat_idx in sorted_material_idxs:
|
||||||
mat = mesh_obj.data.materials[mat_idx]
|
mat = mesh_obj.data.materials[mat_idx]
|
||||||
hecl_str = hmdl_shader.shader(mat, mesh_obj, bpy.data.filepath)
|
hecl_str, texs = hmdl_shader.shader(mat, mesh_obj, bpy.data.filepath)
|
||||||
|
writebuffunc((hecl_str + '\n').encode())
|
||||||
|
writebuffunc(struct.pack('I', len(texs)))
|
||||||
|
for tex in texs:
|
||||||
|
writebuffunc((tex + '\n').encode())
|
||||||
|
|
||||||
mesh_maker = hmdl_mesh.hmdl_mesh()
|
# Output vert pool
|
||||||
|
vert_pool.write_out(writebuffunc, mesh_obj.vertex_groups)
|
||||||
|
|
||||||
# Make special version of mesh with just the relevant material;
|
# Generate island meshes
|
||||||
# Also perform triangulation
|
for mat_idx in sorted_material_idxs:
|
||||||
mesh = bpy.data.meshes.new(copy_obj.name + '_' + str(mat_idx))
|
# Make special version of mesh with just the relevant material
|
||||||
part_meshes.add(mesh)
|
bm = bm_master.copy()
|
||||||
bm = bmesh.new()
|
|
||||||
bm.from_mesh(copy_obj.data)
|
|
||||||
to_remove = []
|
to_remove = []
|
||||||
shader_center = Vector((0,0,0))
|
|
||||||
shader_center_count = 0
|
|
||||||
for face in bm.faces:
|
for face in bm.faces:
|
||||||
if face.material_index != mat_idx:
|
if face.material_index != mat_idx:
|
||||||
to_remove.append(face)
|
to_remove.append(face)
|
||||||
else:
|
|
||||||
shader_center += face.calc_center_bounds()
|
|
||||||
shader_center_count += 1
|
|
||||||
shader_center /= shader_center_count
|
|
||||||
bmesh.ops.delete(bm, geom=to_remove, context=5)
|
bmesh.ops.delete(bm, geom=to_remove, context=5)
|
||||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
vert_pool.set_bm_layers(bm)
|
||||||
|
|
||||||
|
dlay = None
|
||||||
|
if len(bm.verts.layers.deform):
|
||||||
|
dlay = bm.verts.layers.deform[0]
|
||||||
|
|
||||||
|
mat_faces_rem = list(bm.faces)
|
||||||
|
while len(mat_faces_rem):
|
||||||
|
the_list = []
|
||||||
|
skin_slot_set = set()
|
||||||
|
HMDLMesh.recursive_faces_islands(dlay, the_list, mat_faces_rem, skin_slot_set,
|
||||||
|
max_skin_banks, mat_faces_rem[0])
|
||||||
|
writebuffunc(struct.pack('B', 1))
|
||||||
|
HMDLMesh.write_out_surface(writebuffunc, vert_pool, bm, the_list, mat_idx)
|
||||||
|
|
||||||
bm.to_mesh(mesh)
|
bm.to_mesh(mesh)
|
||||||
bm.free()
|
bm.free()
|
||||||
|
|
||||||
# Optimise mesh
|
# No more surfaces
|
||||||
if rigger:
|
writebuffunc(struct.pack('B', 0))
|
||||||
mesh_maker.add_mesh(mesh, rigger, shader_uv_count)
|
|
||||||
else:
|
|
||||||
mesh_maker.add_mesh(mesh, None, shader_uv_count)
|
|
||||||
|
|
||||||
shader_set.append((shader_hashes, mesh_maker, shader_center))
|
|
||||||
|
|
||||||
# Filter out useless AABB points and generate data array
|
# Filter out useless AABB points and generate data array
|
||||||
aabb = bytearray()
|
aabb = bytearray()
|
||||||
for comp in copy_obj.bound_box[0]:
|
for comp in copy_obj.bound_box[0]:
|
||||||
aabb += struct.pack(endian_char + 'f', comp)
|
aabb += struct.pack('f', comp)
|
||||||
for comp in copy_obj.bound_box[6]:
|
for comp in copy_obj.bound_box[6]:
|
||||||
aabb += struct.pack(endian_char + 'f', comp)
|
aabb += struct.pack('f', comp)
|
||||||
|
|
||||||
# Delete copied mesh from scene
|
# Delete copied mesh from scene
|
||||||
bpy.context.scene.objects.unlink(copy_obj)
|
bpy.context.scene.objects.unlink(copy_obj)
|
||||||
bpy.data.objects.remove(copy_obj)
|
bpy.data.objects.remove(copy_obj)
|
||||||
bpy.data.meshes.remove(copy_mesh)
|
bpy.data.meshes.remove(copy_mesh)
|
||||||
|
|
||||||
# Count total collections
|
|
||||||
total_collections = 0
|
|
||||||
for shader in shader_set:
|
|
||||||
total_collections += len(shader[1].collections)
|
|
||||||
|
|
||||||
# Start writing master buffer
|
|
||||||
output_data = bytearray()
|
|
||||||
output_data += aabb
|
|
||||||
output_data += struct.pack(endian_char + 'III', mesh_obj.data.hecl_material_count, len(shader_set), total_collections)
|
|
||||||
|
|
||||||
# Shader Reference Data (truncated SHA1 hashes)
|
|
||||||
if mesh_obj.data.hecl_material_count > 0:
|
|
||||||
for grp_idx in range(mesh_obj.data.hecl_material_count):
|
|
||||||
for shader in shader_set:
|
|
||||||
output_data += shader[0][grp_idx]
|
|
||||||
else:
|
|
||||||
for shader in shader_set:
|
|
||||||
for subshader in shader[0]:
|
|
||||||
output_data += subshader
|
|
||||||
|
|
||||||
# Generate mesh data
|
|
||||||
for shader in shader_set:
|
|
||||||
mesh_maker = shader[1]
|
|
||||||
output_data += struct.pack(endian_char + 'Ifff', len(mesh_maker.collections), shader[2][0], shader[2][1], shader[2][2])
|
|
||||||
for coll_idx in range(len(mesh_maker.collections)):
|
|
||||||
|
|
||||||
# Vert Buffer
|
|
||||||
uv_count, max_bones, vert_bytes, vert_arr = mesh_maker.generate_vertex_buffer(coll_idx, endian_char)
|
|
||||||
output_data += struct.pack(endian_char + 'III', uv_count, max_bones // 4, len(vert_bytes))
|
|
||||||
output_data += vert_bytes
|
|
||||||
|
|
||||||
# Elem Buffer
|
|
||||||
collection_primitives, element_bytes, elem_arr = mesh_maker.generate_element_buffer(coll_idx, endian_char)
|
|
||||||
output_data += struct.pack(endian_char + 'I', len(element_bytes))
|
|
||||||
output_data += element_bytes
|
|
||||||
|
|
||||||
# Index Buffer
|
|
||||||
index_bytes = mesh_maker.generate_index_buffer(collection_primitives, endian_char, rigger)
|
|
||||||
output_data += struct.pack(endian_char + 'I', len(index_bytes))
|
|
||||||
output_data += index_bytes
|
|
||||||
|
|
||||||
# Generate rigging data
|
|
||||||
skin_info = None
|
|
||||||
if rigger:
|
|
||||||
skin_info = rigger.generate_rigging_info(endian_char)
|
|
||||||
|
|
||||||
# Write final buffer
|
|
||||||
final_data = bytearray()
|
|
||||||
final_data = b'HMDL'
|
|
||||||
if rigger:
|
|
||||||
final_data += struct.pack(endian_char + 'IIII', 1, actual_max_texmtx_count, max_bone_count, len(skin_info))
|
|
||||||
final_data += skin_info
|
|
||||||
else:
|
|
||||||
final_data += struct.pack(endian_char + 'II', 0, actual_max_texmtx_count)
|
|
||||||
final_data += output_data
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
for mesh in part_meshes:
|
|
||||||
bpy.data.meshes.remove(mesh)
|
|
||||||
|
|
||||||
# Write final mesh object
|
|
||||||
if area_db_id is not None:
|
|
||||||
new_hash = 0
|
|
||||||
else:
|
|
||||||
new_hash = heclpak.add_object(final_data, b'HMDL', resource_name)
|
|
||||||
res_db.update_resource_stats(db_id, new_hash)
|
|
||||||
|
|
||||||
return db_id, new_hash, final_data
|
|
||||||
|
|
||||||
def draw(layout, context):
|
def draw(layout, context):
|
||||||
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
||||||
|
|
|
@ -143,14 +143,31 @@ def dataout_loop():
|
||||||
|
|
||||||
elif cmdargs[0] == 'MESHCOMPILE':
|
elif cmdargs[0] == 'MESHCOMPILE':
|
||||||
meshName = cmdargs[1]
|
meshName = cmdargs[1]
|
||||||
maxIdx = int(cmdargs[2])
|
maxSkinBanks = int(cmdargs[2])
|
||||||
maxSkinBanks = int(cmdargs[3])
|
|
||||||
|
|
||||||
if meshName not in bpy.data.objects:
|
if meshName not in bpy.data.objects:
|
||||||
writepipeline(b'mesh not found')
|
writepipeline(b'mesh not found')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName])
|
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], maxSkinBanks)
|
||||||
|
|
||||||
|
elif cmdargs[0] == 'MESHCOMPILEALL':
|
||||||
|
maxSkinBanks = int(cmdargs[1])
|
||||||
|
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
join_mesh = bpy.data.meshes.new('JOIN_MESH')
|
||||||
|
join_obj = bpy.data.object.new(join_mesh.name, join_mesh)
|
||||||
|
bpy.context.scene.objects.link(join_obj)
|
||||||
|
bpy.ops.object.select_by_type(type='MESH')
|
||||||
|
bpy.context.scene.objects.active = join_obj
|
||||||
|
bpy.ops.object.join()
|
||||||
|
|
||||||
|
hecl.hmdl.cook(writepipebuf, join_obj, maxSkinBanks)
|
||||||
|
|
||||||
|
bpy.context.scene.objects.unlink(join_obj)
|
||||||
|
bpy.data.objects.remove(join_obj)
|
||||||
|
bpy.data.meshes.remove(join_mesh)
|
||||||
|
|
||||||
|
|
||||||
# Command loop
|
# Command loop
|
||||||
while True:
|
while True:
|
||||||
|
@ -250,7 +267,6 @@ while True:
|
||||||
writepipeline(b'ERROR')
|
writepipeline(b'ERROR')
|
||||||
|
|
||||||
elif cmdargs[0] == 'DATABEGIN':
|
elif cmdargs[0] == 'DATABEGIN':
|
||||||
writepipeline(b'READY')
|
|
||||||
dataout_loop()
|
dataout_loop()
|
||||||
|
|
||||||
elif cmdargs[0] == 'DATAEND':
|
elif cmdargs[0] == 'DATAEND':
|
||||||
|
|
Loading…
Reference in New Issue