Add collision mesh cooking to BlenderConnection

This commit is contained in:
Jack Andersen 2016-08-10 11:54:30 -10:00
parent 73cb100174
commit 2b1e246ae9
5 changed files with 212 additions and 6 deletions

View File

@ -871,6 +871,54 @@ uint32_t BlenderConnection::DataStream::Mesh::SkinBanks::addSurface
return uint32_t(-1); return uint32_t(-1);
} }
BlenderConnection::DataStream::ColMesh::ColMesh(BlenderConnection& conn)
: sceneXf(conn), aabbMin(conn), aabbMax(conn)
{
uint32_t matCount;
conn._readBuf(&matCount, 4);
materials.reserve(matCount);
for (uint32_t i=0 ; i<matCount ; ++i)
materials.emplace_back(conn);
uint32_t count;
conn._readBuf(&count, 4);
verts.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
verts.emplace_back(conn);
conn._readBuf(&count, 4);
edges.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
edges.emplace_back(conn);
conn._readBuf(&count, 4);
trianges.reserve(count);
for (uint32_t i=0 ; i<count ; ++i)
trianges.emplace_back(conn);
}
BlenderConnection::DataStream::ColMesh::Material::Material(BlenderConnection& conn)
{
uint32_t nameLen;
conn._readBuf(&nameLen, 4);
if (nameLen)
{
name.assign(nameLen, '\0');
conn._readBuf(&name[0], nameLen);
}
conn._readBuf(&type, 5);
}
BlenderConnection::DataStream::ColMesh::Edge::Edge(BlenderConnection& conn)
{
conn._readBuf(this, 9);
}
BlenderConnection::DataStream::ColMesh::Triangle::Triangle(BlenderConnection& conn)
{
conn._readBuf(this, 16);
}
BlenderConnection::DataStream::Actor::Actor(BlenderConnection& conn) BlenderConnection::DataStream::Actor::Actor(BlenderConnection& conn)
{ {
uint32_t armCount; uint32_t armCount;
@ -1084,6 +1132,25 @@ BlenderConnection::DataStream::compileMesh(const std::string& name,
return Mesh(*m_parent, topology, skinSlotCount, surfProg); return Mesh(*m_parent, topology, skinSlotCount, surfProg);
} }
BlenderConnection::DataStream::ColMesh
BlenderConnection::DataStream::compileColMesh(const std::string& name)
{
if (m_parent->m_loadedType != BlendType::Area)
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
m_parent->m_loadedBlend.getAbsolutePath().c_str());
char req[128];
snprintf(req, 128, "MESHCOMPILENAMECOLLISION %s", name.c_str());
m_parent->_writeLine(req);
char readBuf[256];
m_parent->_readLine(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(logvisor::Fatal, "unable to cook collision mesh '%s': %s", name.c_str(), readBuf);
return ColMesh(*m_parent);
}
BlenderConnection::DataStream::Mesh BlenderConnection::DataStream::Mesh
BlenderConnection::DataStream::compileAllMeshes(HMDLTopology topology, BlenderConnection::DataStream::compileAllMeshes(HMDLTopology topology,
int skinSlotCount, int skinSlotCount,

View File

@ -371,6 +371,8 @@ public:
operator const uint32_t&() const {return val;} operator const uint32_t&() const {return val;}
}; };
static atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec);
/** Intermediate mesh representation prepared by blender from a single mesh object */ /** Intermediate mesh representation prepared by blender from a single mesh object */
struct Mesh struct Mesh
{ {
@ -517,7 +519,48 @@ public:
/** Prepares mesh representation for indexed access on modern APIs. /** Prepares mesh representation for indexed access on modern APIs.
* Mesh must remain resident for accessing reference members * Mesh must remain resident for accessing reference members
*/ */
HMDLBuffers getHMDLBuffers() const; HMDLBuffers getHMDLBuffers(bool absoluteCoords) const;
};
/** Intermediate collision mesh representation prepared by blender from a single mesh object */
struct ColMesh
{
/* Object transform in scene */
Matrix4f sceneXf;
/* Cumulative AABB */
Vector3f aabbMin;
Vector3f aabbMax;
/** HECL source and metadata of each material */
struct Material
{
std::string name;
uint32_t type;
bool fireThrough;
Material(BlenderConnection& conn);
};
std::vector<Material> materials;
std::vector<Vector3f> verts;
struct Edge
{
uint32_t verts[2];
bool seam;
Edge(BlenderConnection& conn);
};
std::vector<Edge> edges;
struct Triangle
{
uint32_t edges[3];
uint32_t matIdx;
Triangle(BlenderConnection& conn);
};
std::vector<Triangle> trianges;
ColMesh(BlenderConnection& conn);
}; };
@ -536,6 +579,9 @@ public:
Mesh compileMesh(const std::string& name, HMDLTopology topology, int skinSlotCount=10, Mesh compileMesh(const std::string& name, HMDLTopology topology, int skinSlotCount=10,
Mesh::SurfProgFunc surfProg=[](int){}); Mesh::SurfProgFunc surfProg=[](int){});
/** Compile collision mesh by name (AREA blends only) */
ColMesh compileColMesh(const std::string& name);
/** Compile all meshes into one (AREA blends only) */ /** Compile all meshes into one (AREA blends only) */
Mesh compileAllMeshes(HMDLTopology topology, int skinSlotCount=10, float maxOctantLength=5.0, Mesh compileAllMeshes(HMDLTopology topology, int skinSlotCount=10, float maxOctantLength=5.0,
Mesh::SurfProgFunc surfProg=[](int){}); Mesh::SurfProgFunc surfProg=[](int){});

View File

@ -3,7 +3,16 @@
namespace hecl namespace hecl
{ {
HMDLBuffers BlenderConnection::DataStream::Mesh::getHMDLBuffers() const atVec3f BlenderConnection::DataStream::MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec)
{
atVec3f res;
res.vec[0] = mtx[0].vec[0] * vec.val.vec[0] + mtx[0].vec[1] * vec.val.vec[1] + mtx[0].vec[2] * vec.val.vec[2] + mtx[0].vec[3];
res.vec[1] = mtx[1].vec[0] * vec.val.vec[0] + mtx[1].vec[1] * vec.val.vec[1] + mtx[1].vec[2] * vec.val.vec[2] + mtx[1].vec[3];
res.vec[2] = mtx[2].vec[0] * vec.val.vec[0] + mtx[2].vec[1] * vec.val.vec[1] + mtx[2].vec[2] * vec.val.vec[2] + mtx[2].vec[3];
return res;
}
HMDLBuffers BlenderConnection::DataStream::Mesh::getHMDLBuffers(bool absoluteCoords) const
{ {
/* If skinned, compute max weight vec count */ /* If skinned, compute max weight vec count */
size_t weightCount = 0; size_t weightCount = 0;
@ -75,6 +84,12 @@ HMDLBuffers BlenderConnection::DataStream::Mesh::getHMDLBuffers() const
const Surface& s = *sv.first; const Surface& s = *sv.first;
const Surface::Vert& v = *sv.second; const Surface::Vert& v = *sv.second;
if (absoluteCoords)
{
atVec3f preXfPos = MtxVecMul4RM(sceneXf, pos[v.iPos]);
vboW.writeVec3fLittle(preXfPos);
}
else
vboW.writeVec3fLittle(pos[v.iPos]); vboW.writeVec3fLittle(pos[v.iPos]);
vboW.writeVec3fLittle(norm[v.iNorm]); vboW.writeVec3fLittle(norm[v.iNorm]);

View File

@ -26,8 +26,7 @@ def write_out_material(writebuf, mat, mesh_obj):
writebuf(struct.pack('i', prop[1])) writebuf(struct.pack('i', prop[1]))
# 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
# into the HECL data-pipeline and returning a hash once complete
def cook(writebuf, mesh_obj, output_mode, max_skin_banks, max_octant_length=None): def cook(writebuf, mesh_obj, output_mode, max_skin_banks, max_octant_length=None):
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)
@ -205,6 +204,76 @@ def cook(writebuf, mesh_obj, output_mode, max_skin_banks, max_octant_length=None
bpy.data.objects.remove(copy_obj) bpy.data.objects.remove(copy_obj)
bpy.data.meshes.remove(copy_mesh) bpy.data.meshes.remove(copy_mesh)
# Takes a Blender 'Mesh' object (not the datablock)
# and performs a one-shot conversion process to collision geometry
def cookcol(writebuf, mesh_obj):
if mesh_obj.type != 'MESH':
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
# Copy mesh (and apply mesh modifiers with triangulation)
copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new(copy_name)
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.data = mesh_obj.to_mesh(bpy.context.scene, True, 'RENDER')
copy_mesh = copy_obj.data
copy_obj.scale = mesh_obj.scale
bpy.context.scene.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = copy_obj
copy_obj.select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.context.scene.update()
bpy.ops.object.mode_set(mode='OBJECT')
copy_mesh.calc_normals_split()
rna_loops = copy_mesh.loops
# Send scene matrix
wmtx = mesh_obj.matrix_world
writebuf(struct.pack('ffffffffffffffff',
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
# Filter out useless AABB points and send data
pt = copy_obj.bound_box[0]
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
pt = copy_obj.bound_box[6]
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
# Send materials
writebuf(struct.pack('I', len(copy_mesh.materials)))
for m in copy_mesh.materials:
writebuf(struct.pack('I', len(m.name)))
writebuf(m.name.encode())
writebuf(struct.pack('Ib', m.retro_collision_type, m.retro_projectile_passthrough))
# Send verts
writebuf(struct.pack('I', len(copy_mesh.vertices)))
for v in copy_mesh.vertices:
writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2]))
# Send edges
writebuf(struct.pack('I', len(copy_mesh.edges)))
for e in copy_mesh.edges:
writebuf(struct.pack('IIb', e.vertices[0], e.vertices[1], e.use_seam))
# Send trianges
writebuf(struct.pack('I', len(copy_mesh.polygons)))
for p in copy_mesh.polygons:
edge_idxs = []
for ek in p.edge_keys:
edge_idxs.append(copy_mesh.edge_keys.index(ek))
writebuf(struct.pack('IIII', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index))
# Delete copied mesh from scene
bpy.context.scene.objects.unlink(copy_obj)
bpy.data.objects.remove(copy_obj)
bpy.data.meshes.remove(copy_mesh)
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')

View File

@ -182,6 +182,16 @@ def dataout_loop():
writepipeline(b'OK') writepipeline(b'OK')
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], cmdargs[2], maxSkinBanks) hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], cmdargs[2], maxSkinBanks)
elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION':
meshName = cmdargs[1]
if meshName not in bpy.data.objects:
writepipeline(('mesh %s not found' % meshName).encode())
continue
writepipeline(b'OK')
hecl.hmdl.cookcol(writepipebuf, bpy.data.objects[meshName])
elif cmdargs[0] == 'MESHCOMPILEALL': elif cmdargs[0] == 'MESHCOMPILEALL':
maxSkinBanks = int(cmdargs[2]) maxSkinBanks = int(cmdargs[2])
maxOctantLength = float(cmdargs[3]) maxOctantLength = float(cmdargs[3])
@ -238,7 +248,6 @@ def dataout_loop():
for c in r: for c in r:
writepipebuf(struct.pack('f', c)) writepipebuf(struct.pack('f', c))
# Main exception handling # Main exception handling
try: try:
# Command loop # Command loop