mirror of https://github.com/AxioDL/metaforce.git
Add collision mesh cooking to BlenderConnection
This commit is contained in:
parent
73cb100174
commit
2b1e246ae9
|
@ -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,
|
||||||
|
|
|
@ -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){});
|
||||||
|
|
|
@ -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,7 +84,13 @@ 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;
|
||||||
|
|
||||||
vboW.writeVec3fLittle(pos[v.iPos]);
|
if (absoluteCoords)
|
||||||
|
{
|
||||||
|
atVec3f preXfPos = MtxVecMul4RM(sceneXf, pos[v.iPos]);
|
||||||
|
vboW.writeVec3fLittle(preXfPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vboW.writeVec3fLittle(pos[v.iPos]);
|
||||||
vboW.writeVec3fLittle(norm[v.iNorm]);
|
vboW.writeVec3fLittle(norm[v.iNorm]);
|
||||||
|
|
||||||
for (size_t i=0 ; i<colorLayerCount ; ++i)
|
for (size_t i=0 ; i<colorLayerCount ; ++i)
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue