Initial blender addon support for MAPA and MAPU types

This commit is contained in:
Jack Andersen 2017-03-19 19:08:51 -10:00
parent 9cc364c4fd
commit 53521a0eea
8 changed files with 427 additions and 4 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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')

View File

@ -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')

View File

@ -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

2
hecl/extern/boo vendored

@ -1 +1 @@
Subproject commit 5f903c09eead505d8af39b8b5c3c1f2550477f97
Subproject commit 9a7cadce3a7de43cb9b3823439e97071395015b7

View File

@ -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()
{

View File

@ -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];