mirror of https://github.com/AxioDL/metaforce.git
Initial blender addon support for MAPA and MAPU types
This commit is contained in:
parent
9cc364c4fd
commit
53521a0eea
|
@ -146,6 +146,8 @@ def register():
|
|||
sact.register()
|
||||
srea.register()
|
||||
frme.register()
|
||||
mapa.register()
|
||||
mapu.register()
|
||||
bpy.utils.register_class(hecl_scene_panel)
|
||||
bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True)
|
||||
bpy.app.handlers.load_post.append(scene_loaded)
|
||||
|
|
|
@ -95,6 +95,11 @@ class VertPool:
|
|||
for ent in entries:
|
||||
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
|
||||
|
||||
def write_out_map(self, writebuf):
|
||||
writebuf(struct.pack('I', len(self.pos)))
|
||||
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
|
||||
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
|
||||
|
||||
def get_pos_idx(self, vert):
|
||||
pf = vert.co.copy().freeze()
|
||||
return self.pos[pf]
|
||||
|
@ -160,6 +165,15 @@ class VertPool:
|
|||
sp = struct.pack('I', self.get_skin_idx(loop.vert))
|
||||
writebuf(sp)
|
||||
|
||||
def loop_out_map(self, writebuf, loop):
|
||||
writebuf(struct.pack('B', 1))
|
||||
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
|
||||
|
||||
def vert_out_map(self, writebuf, vert):
|
||||
writebuf(struct.pack('B', 1))
|
||||
writebuf(struct.pack('I', self.get_pos_idx(vert)))
|
||||
|
||||
|
||||
def sort_faces_by_skin_group(dlay, faces):
|
||||
faces_out = []
|
||||
done_sg = set()
|
||||
|
|
|
@ -1,2 +1,167 @@
|
|||
import bpy, struct
|
||||
from . import hmdl
|
||||
from mathutils import Vector
|
||||
VertPool = hmdl.HMDLMesh.VertPool
|
||||
|
||||
def cook(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
|
||||
|
||||
# Create master BMesh and VertPool
|
||||
bm_master = bmesh.new()
|
||||
bm_master.from_mesh(copy_obj.data)
|
||||
vert_pool = VertPool(bm_master, rna_loops)
|
||||
|
||||
# Output vert pool
|
||||
vert_pool.write_out_map(writebuf)
|
||||
|
||||
# Create map surfaces and borders
|
||||
island_faces = list(bm_master.faces)
|
||||
prev_loop_emit = None
|
||||
out_count = 0
|
||||
loop_ranges = []
|
||||
loop_iter = 0
|
||||
while len(island_faces):
|
||||
sel_lists_local = []
|
||||
restore_out_count = out_count
|
||||
for start_face in island_faces:
|
||||
for l in start_face.loops:
|
||||
out_count = restore_out_count
|
||||
island_local = list(island_faces)
|
||||
if out_count & 1:
|
||||
prev_loop = l.link_loop_prev
|
||||
loop = prev_loop.link_loop_prev
|
||||
sel_list = [l, prev_loop, loop]
|
||||
prev_loop = loop
|
||||
else:
|
||||
prev_loop = l.link_loop_next
|
||||
loop = prev_loop.link_loop_next
|
||||
sel_list = [l, prev_loop, loop]
|
||||
out_count += 3
|
||||
island_local.remove(start_face)
|
||||
while True:
|
||||
if not prev_loop.edge.is_contiguous or prev_loop.edge.seam:
|
||||
break
|
||||
loop, prev_loop = strip_next_loop(prev_loop, out_count)
|
||||
face = loop.face
|
||||
if face not in island_local:
|
||||
break
|
||||
sel_list.append(loop)
|
||||
island_local.remove(face)
|
||||
out_count += 1
|
||||
sel_lists_local.append((sel_list, island_local, out_count))
|
||||
max_count = 0
|
||||
max_sl = None
|
||||
max_island_faces = None
|
||||
for sl in sel_lists_local:
|
||||
if len(sl[0]) > max_count:
|
||||
max_count = len(sl[0])
|
||||
max_sl = sl[0]
|
||||
max_island_faces = sl[1]
|
||||
out_count = sl[2]
|
||||
island_faces = max_island_faces
|
||||
|
||||
loop_set = set()
|
||||
edge_set = set()
|
||||
loop_count = len(max_sl)
|
||||
if prev_loop_emit:
|
||||
vert_pool.loop_out_map(writebuf, prev_loop_emit)
|
||||
vert_pool.loop_out_map(writebuf, max_sl[0])
|
||||
loop_count += 2
|
||||
loop_set.add(prev_loop_emit)
|
||||
loop_set.add(max_sl[0])
|
||||
for loop in max_sl:
|
||||
vert_pool.loop_out_map(writebuf, loop)
|
||||
prev_loop_emit = loop
|
||||
loop_set.add(loop)
|
||||
for edge in loop.face.edges:
|
||||
if edge.seam:
|
||||
edge_set.add(edge)
|
||||
|
||||
trace_edge = edge_set.pop()
|
||||
edge_iter = loop_iter + loop_count
|
||||
edge_count = 0
|
||||
if trace_edge:
|
||||
vert_pool.vert_out_map(writebuf, trace_edge.verts[0])
|
||||
vert_pool.vert_out_map(writebuf, trace_edge.verts[1])
|
||||
edge_count += 2
|
||||
next_vert = trace_edge.verts[1]
|
||||
found_edge = True
|
||||
while found_edge:
|
||||
found_edge = False
|
||||
for edge in next_vert.link_edges:
|
||||
if edge in edge_set:
|
||||
edge_set.remove(edge)
|
||||
next_vert = edge.other_vert(next_vert)
|
||||
vert_pool.vert_out_map(writebuf, next_vert)
|
||||
edge_count += 1
|
||||
found_edge = True
|
||||
break
|
||||
|
||||
pos_avg = Vector()
|
||||
norm_avg = Vector()
|
||||
if len(loop_set):
|
||||
for loop in loop_set:
|
||||
pos_avg += loop.co
|
||||
norm_avg += loop.normal
|
||||
pos_avg /= len(loop_set)
|
||||
norm_avg /= len(loop_set)
|
||||
norm_avg.normalize()
|
||||
|
||||
loop_ranges.append((loop_iter, loop_count, edge_iter, edge_count, pos_avg, norm_avg))
|
||||
loop_iter += loop_count + edge_count
|
||||
|
||||
# No more surfaces
|
||||
writebuf(struct.pack('B', 0))
|
||||
|
||||
# Write out loop ranges and averages
|
||||
writebuf(struct.pack('I', len(loop_ranges)))
|
||||
for loop_range in loop_ranges:
|
||||
writebuf(struct.pack('fff', loop_range[4][0], loop_range[4][1], loop_range[4][2]))
|
||||
writebuf(struct.pack('fff', loop_range[5][0], loop_range[5][1], loop_range[5][2]))
|
||||
writebuf(struct.pack('IIII', loop_range[0], loop_range[1], loop_range[2], loop_range[3]))
|
||||
|
||||
# Write out mappable objects
|
||||
poi_count = 0
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.retro_mappable_type != -1:
|
||||
poi_count += 1
|
||||
|
||||
writebuf(struct.pack('I', poi_count))
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.retro_mappable_type != -1:
|
||||
writebuf(struct.pack('III',
|
||||
obj.retro_mappable_type, obj.retro_mappable_unk, obj.retro_mappable_sclyid))
|
||||
writebuf(struct.pack('ffffffffffffffff',
|
||||
obj.matrix_world[0][0], obj.matrix_world[0][1], obj.matrix_world[0][2], obj.matrix_world[0][3],
|
||||
obj.matrix_world[1][0], obj.matrix_world[1][1], obj.matrix_world[1][2], obj.matrix_world[1][3],
|
||||
obj.matrix_world[2][0], obj.matrix_world[2][1], obj.matrix_world[2][2], obj.matrix_world[2][3],
|
||||
obj.matrix_world[3][0], obj.matrix_world[3][1], obj.matrix_world[3][2], obj.matrix_world[3][3]))
|
||||
|
||||
def draw(layout, context):
|
||||
pass
|
||||
|
||||
def register():
|
||||
bpy.types.Object.retro_mappable_type = bpy.props.IntProperty(name='Retro: MAPA object type', default=-1)
|
||||
bpy.types.Object.retro_mappable_unk = bpy.props.IntProperty(name='Retro: MAPA object unk')
|
||||
bpy.types.Object.retro_mappable_sclyid = bpy.props.StringProperty(name='Retro: MAPA object SCLY ID')
|
||||
|
|
|
@ -1,2 +1,49 @@
|
|||
import bpy
|
||||
|
||||
def cook(writebuf):
|
||||
found_lib = False
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.library:
|
||||
path = os.path.normpath(bpy.path.abspath(obj.library.filepath))
|
||||
writebuf(struct.pack('I', len(path)))
|
||||
writebuf(path.encode())
|
||||
found_lib = True
|
||||
break
|
||||
if not found_lib:
|
||||
raise RuntimeError('No hexagon segments present')
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if not obj.parent and obj.type == 'EMPTY':
|
||||
writebuf(struct.pack('I', len(obj.name)))
|
||||
writebuf(obj.name.encode())
|
||||
writebuf(struct.pack('ffffffffffffffff',
|
||||
obj.matrix_local[0][0], obj.matrix_local[0][1], obj.matrix_local[0][2], obj.matrix_local[0][3],
|
||||
obj.matrix_local[1][0], obj.matrix_local[1][1], obj.matrix_local[1][2], obj.matrix_local[1][3],
|
||||
obj.matrix_local[2][0], obj.matrix_local[2][1], obj.matrix_local[2][2], obj.matrix_local[2][3],
|
||||
obj.matrix_local[3][0], obj.matrix_local[3][1], obj.matrix_local[3][2], obj.matrix_local[3][3]))
|
||||
writebuf(struct.pack('I', len(obj.children)))
|
||||
for child in obj.children:
|
||||
writebuf(struct.pack('ffffffffffffffff',
|
||||
child.matrix_local[0][0], child.matrix_local[0][1], child.matrix_local[0][2], child.matrix_local[0][3],
|
||||
child.matrix_local[1][0], child.matrix_local[1][1], child.matrix_local[1][2], child.matrix_local[1][3],
|
||||
child.matrix_local[2][0], child.matrix_local[2][1], child.matrix_local[2][2], child.matrix_local[2][3],
|
||||
child.matrix_local[3][0], child.matrix_local[3][1], child.matrix_local[3][2], child.matrix_local[3][3]))
|
||||
writebuf(struct.pack('ffff', obj.retro_mapworld_color[0], obj.retro_mapworld_color[1],
|
||||
obj.retro_mapworld_color[2], obj.retro_mapworld_color[3]))
|
||||
writebuf(struct.pack('I', len(obj.retro_mapworld_path)))
|
||||
writebuf(obj.retro_mapworld_path.encode())
|
||||
|
||||
def draw(layout, context):
|
||||
pass
|
||||
obj = context.active_object
|
||||
if not obj:
|
||||
return
|
||||
while obj.parent:
|
||||
obj = obj.parent
|
||||
layout.prop(obj, 'retro_mapworld_color', text='Color')
|
||||
layout.prop(obj, 'retro_mapworld_path', text='Path')
|
||||
|
||||
# Registration
|
||||
def register():
|
||||
bpy.types.Object.retro_mapworld_color = bpy.props.FloatVectorProperty(name='Retro: MapWorld Color',\
|
||||
description='Sets map world color', subtype='COLOR', size=4, min=0.0, max=1.0)
|
||||
bpy.types.Object.retro_mapworld_path = bpy.props.StringProperty(name='Retro: MapWorld Path', description='Sets path to World root')
|
||||
|
|
|
@ -417,6 +417,21 @@ def dataout_loop():
|
|||
hecl.srea.render_pvs_light(pathOut, lightName)
|
||||
writepipestr(b'OK')
|
||||
|
||||
elif cmdargs[0] == 'MAPAREACOMPILE':
|
||||
if 'MAP' not in bpy.data.objects:
|
||||
writepipestr(('"MAP" object not in .blend').encode())
|
||||
continue
|
||||
map_obj = bpy.data.objects['MAP']
|
||||
if map_obj.type != 'MESH':
|
||||
writepipestr(('object "MAP" not a MESH').encode())
|
||||
continue
|
||||
writepipestr(b'OK')
|
||||
hecl.mapa.cook(writepipebuf, map_obj)
|
||||
|
||||
elif cmdargs[0] == 'MAPUNIVERSECOMPILE':
|
||||
writepipestr(b'OK')
|
||||
hecl.mapu.cook(writepipebuf)
|
||||
|
||||
loaded_blend = None
|
||||
|
||||
# Main exception handling
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 5f903c09eead505d8af39b8b5c3c1f2550477f97
|
||||
Subproject commit 9a7cadce3a7de43cb9b3823439e97071395015b7
|
|
@ -715,6 +715,51 @@ public:
|
|||
Light(BlenderConnection& conn);
|
||||
};
|
||||
|
||||
/** Intermediate MapArea representation */
|
||||
struct MapArea
|
||||
{
|
||||
std::vector<Vector3f> verts;
|
||||
std::vector<Index> indices;
|
||||
struct Surface
|
||||
{
|
||||
Vector3f normal;
|
||||
Vector3f centerOfMass;
|
||||
Index start;
|
||||
Index count;
|
||||
Index borderStart;
|
||||
Index borderCount;
|
||||
Surface(BlenderConnection& conn);
|
||||
};
|
||||
std::vector<Surface> surfaces;
|
||||
struct POI
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t unk;
|
||||
uint32_t objid;
|
||||
Matrix4f xf;
|
||||
POI(BlenderConnection& conn);
|
||||
};
|
||||
std::vector<POI> pois;
|
||||
MapArea(BlenderConnection& conn);
|
||||
};
|
||||
|
||||
/** Intermediate MapUniverse representation */
|
||||
struct MapUniverse
|
||||
{
|
||||
hecl::ProjectPath hexagonPath;
|
||||
struct World
|
||||
{
|
||||
std::string name;
|
||||
Matrix4f xf;
|
||||
std::vector<Matrix4f> hexagons;
|
||||
Vector4f color;
|
||||
hecl::ProjectPath worldPath;
|
||||
World(BlenderConnection& conn);
|
||||
};
|
||||
std::vector<World> worlds;
|
||||
MapUniverse(BlenderConnection& conn);
|
||||
};
|
||||
|
||||
static const char* MeshOutputModeString(HMDLTopology topology)
|
||||
{
|
||||
static const char* STRS[] = {"TRIANGLES", "TRISTRIPS"};
|
||||
|
@ -854,6 +899,9 @@ public:
|
|||
|
||||
bool renderPvs(const std::string& path, const atVec3f& location);
|
||||
bool renderPvsLight(const std::string& path, const std::string& lightName);
|
||||
|
||||
MapArea compileMapArea();
|
||||
MapUniverse compileMapUniverse();
|
||||
};
|
||||
DataStream beginData()
|
||||
{
|
||||
|
|
|
@ -1094,6 +1094,104 @@ BlenderConnection::DataStream::Light::Light(BlenderConnection& conn)
|
|||
}
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapArea::Surface::Surface(BlenderConnection& conn)
|
||||
{
|
||||
centerOfMass.read(conn);
|
||||
normal.read(conn);
|
||||
conn._readBuf(&start, 16);
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapArea::POI::POI(BlenderConnection& conn)
|
||||
{
|
||||
conn._readBuf(&type, 12);
|
||||
xf.read(conn);
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapArea::MapArea(BlenderConnection& conn)
|
||||
{
|
||||
uint32_t vertCount;
|
||||
conn._readBuf(&vertCount, 4);
|
||||
verts.reserve(vertCount);
|
||||
for (int i=0 ; i<vertCount ; ++i)
|
||||
verts.emplace_back(conn);
|
||||
|
||||
uint8_t isIdx;
|
||||
conn._readBuf(&isIdx, 1);
|
||||
while (isIdx)
|
||||
{
|
||||
indices.emplace_back(conn);
|
||||
conn._readBuf(&isIdx, 1);
|
||||
}
|
||||
|
||||
uint32_t surfCount;
|
||||
conn._readBuf(&surfCount, 4);
|
||||
surfaces.reserve(surfCount);
|
||||
for (int i=0 ; i<surfCount ; ++i)
|
||||
surfaces.emplace_back(conn);
|
||||
|
||||
uint32_t poiCount;
|
||||
conn._readBuf(&poiCount, 4);
|
||||
pois.reserve(poiCount);
|
||||
for (int i=0 ; i<poiCount ; ++i)
|
||||
pois.emplace_back(conn);
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapUniverse::World::World(BlenderConnection& conn)
|
||||
{
|
||||
uint32_t nameLen;
|
||||
conn._readBuf(&nameLen, 4);
|
||||
if (nameLen)
|
||||
{
|
||||
name.assign(nameLen, '\0');
|
||||
conn._readBuf(&name[0], nameLen);
|
||||
}
|
||||
|
||||
xf.read(conn);
|
||||
|
||||
uint32_t hexCount;
|
||||
conn._readBuf(&hexCount, 4);
|
||||
hexagons.reserve(hexCount);
|
||||
for (int i=0 ; i<hexCount ; ++i)
|
||||
hexagons.emplace_back(conn);
|
||||
|
||||
color.read(conn);
|
||||
|
||||
uint32_t pathLen;
|
||||
conn._readBuf(&pathLen, 4);
|
||||
if (pathLen)
|
||||
{
|
||||
std::string path;
|
||||
path.assign(pathLen, '\0');
|
||||
conn._readBuf(&path[0], pathLen);
|
||||
|
||||
SystemString pathRel =
|
||||
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(path);
|
||||
worldPath.assign(conn.m_loadedBlend.getProject().getProjectWorkingPath(), pathRel);
|
||||
}
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapUniverse::MapUniverse(BlenderConnection& conn)
|
||||
{
|
||||
uint32_t pathLen;
|
||||
conn._readBuf(&pathLen, 4);
|
||||
if (pathLen)
|
||||
{
|
||||
std::string path;
|
||||
path.assign(pathLen, '\0');
|
||||
conn._readBuf(&path[0], pathLen);
|
||||
|
||||
SystemString pathRel =
|
||||
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(path);
|
||||
hexagonPath.assign(conn.m_loadedBlend.getProject().getProjectWorkingPath(), pathRel);
|
||||
}
|
||||
|
||||
uint32_t worldCount;
|
||||
conn._readBuf(&worldCount, 4);
|
||||
worlds.reserve(worldCount);
|
||||
for (int i=0 ; i<worldCount ; ++i)
|
||||
worlds.emplace_back(conn);
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::Actor::Actor(BlenderConnection& conn)
|
||||
{
|
||||
uint32_t armCount;
|
||||
|
@ -1673,7 +1771,7 @@ bool BlenderConnection::DataStream::renderPvs(const std::string& path, const atV
|
|||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable to render PVS for: %s; %s",
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str(), readBuf);
|
||||
m_parent->m_loadedBlend.getAbsolutePathUTF8().c_str(), readBuf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1695,11 +1793,45 @@ bool BlenderConnection::DataStream::renderPvsLight(const std::string& path, cons
|
|||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable to render PVS light %s for: %s; %s", lightName.c_str(),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str(), readBuf);
|
||||
m_parent->m_loadedBlend.getAbsolutePathUTF8().c_str(), readBuf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapArea BlenderConnection::DataStream::compileMapArea()
|
||||
{
|
||||
if (m_parent->m_loadedType != BlendType::MapArea)
|
||||
BlenderLog.report(logvisor::Fatal, _S("%s is not a MAPAREA blend"),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str());
|
||||
|
||||
m_parent->_writeStr("MAPAREACOMPILE");
|
||||
|
||||
char readBuf[256];
|
||||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable to compile map area: %s; %s",
|
||||
m_parent->m_loadedBlend.getAbsolutePathUTF8().c_str(), readBuf);
|
||||
|
||||
return {*m_parent};
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::MapUniverse BlenderConnection::DataStream::compileMapUniverse()
|
||||
{
|
||||
if (m_parent->m_loadedType != BlendType::MapUniverse)
|
||||
BlenderLog.report(logvisor::Fatal, _S("%s is not a MAPUNIVERSE blend"),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str());
|
||||
|
||||
m_parent->_writeStr("MAPUNIVERSECOMPILE");
|
||||
|
||||
char readBuf[256];
|
||||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable to compile map universe: %s; %s",
|
||||
m_parent->m_loadedBlend.getAbsolutePathUTF8().c_str(), readBuf);
|
||||
|
||||
return {*m_parent};
|
||||
}
|
||||
|
||||
void BlenderConnection::quitBlender()
|
||||
{
|
||||
char lineBuf[256];
|
||||
|
|
Loading…
Reference in New Issue