From 06797cd9fc0a2fa56936a7acd74279d6ddedffff Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 23 Feb 2018 20:15:12 -1000 Subject: [PATCH] Add PATH .blend file type --- hecl/blender/CMakeLists.txt | 3 +- hecl/blender/hecl/__init__.py | 16 ++- hecl/blender/hecl/path.py | 121 +++++++++++++++++++++++ hecl/blender/hecl_blendershell.py | 9 ++ hecl/extern/athena | 2 +- hecl/include/hecl/Blender/Connection.hpp | 11 ++- hecl/include/hecl/hecl.hpp | 4 +- hecl/lib/Blender/Connection.cpp | 67 ++++++++++--- 8 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 hecl/blender/hecl/path.py diff --git a/hecl/blender/CMakeLists.txt b/hecl/blender/CMakeLists.txt index 8e2be2513..6bf6d8052 100644 --- a/hecl/blender/CMakeLists.txt +++ b/hecl/blender/CMakeLists.txt @@ -12,7 +12,8 @@ list(APPEND PY_SOURCES hecl/swld/__init__.py hecl/mapa.py hecl/mapu.py - hecl/frme.py) + hecl/frme.py + hecl/path.py) bintoc(hecl_blendershell.cpp hecl_blendershell.py HECL_BLENDERSHELL) diff --git a/hecl/blender/hecl/__init__.py b/hecl/blender/hecl/__init__.py index ec9f7e3eb..dc39b7784 100644 --- a/hecl/blender/hecl/__init__.py +++ b/hecl/blender/hecl/__init__.py @@ -9,7 +9,7 @@ bl_info = { "category": "System"} # Package import -from . import hmdl, sact, srea, swld, mapa, mapu, frme, Nodegrid, Patching +from . import hmdl, sact, srea, swld, mapa, mapu, frme, path, Nodegrid, Patching Nodegrid = Nodegrid.Nodegrid import bpy, os, sys, struct from bpy.app.handlers import persistent @@ -26,7 +26,8 @@ hecl_typeS = [ ('WORLD', "World", "Active scene represents a HECL World", swld.draw), ('MAPAREA', "Map Area", "Active scene represents a HECL Map Area", mapa.draw), ('MAPUNIVERSE', "Map Universe", "Active scene represents a HECL Map Universe", mapu.draw), -('FRAME', "Gui Frame", "Active scene represents a HECL Gui Frame", frme.draw)] +('FRAME', "Gui Frame", "Active scene represents a HECL Gui Frame", frme.draw), +('PATH', "Path Mesh", "Active scene represents a HECL Path Mesh", path.draw)] # Main Scene Panel class hecl_scene_panel(bpy.types.Panel): @@ -115,6 +116,15 @@ def scene_loaded(dummy): if o.library: o.hide = True + # Show PATH library objects as wireframes + if bpy.context.scene.hecl_type == 'PATH': + if bpy.context.scene.background_set: + for o in bpy.context.scene.background_set.objects: + o.draw_type = 'WIRE' + if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects: + path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj] + path_obj.show_wire = True + # Linked-Child Detection for scene in bpy.data.scenes: if scene.hecl_type == 'ACTOR': @@ -149,6 +159,7 @@ def register(): frme.register() mapa.register() mapu.register() + path.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) @@ -159,6 +170,7 @@ def unregister(): hmdl.unregister() sact.unregister() srea.unregister() + path.unregister() bpy.utils.unregister_class(hecl_scene_panel) Patching.unregister() diff --git a/hecl/blender/hecl/path.py b/hecl/blender/hecl/path.py new file mode 100644 index 000000000..23c45b7b2 --- /dev/null +++ b/hecl/blender/hecl/path.py @@ -0,0 +1,121 @@ +import bpy + +def draw(layout, context): + layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects') + layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon='MANIPUL') + layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='WIRE') + +def cook(writebuf, mesh_obj): + pass + +import bpy, bgl +from mathutils import Vector, Matrix +from mathutils.geometry import intersect_ray_tri, tessellate_polygon + +def correlate_polygon_heights(context, obj): + ret = {} + if obj.type != 'MESH': + return ret + for p in obj.data.polygons: + pl = [obj.matrix_world * obj.data.vertices[vert].co for vert in p.vertices] + tpl = tessellate_polygon((pl,)) + found = False + for eobj in context.scene.objects: + for tri in tpl: + if eobj.type == 'EMPTY' and eobj.library is None: + intersect = intersect_ray_tri(pl[tri[0]], pl[tri[1]], pl[tri[2]], + Vector((0.0, 0.0, -999.0)), + eobj.location) + if intersect is not None: + ret[p] = abs(intersect.z - eobj.location.z) + found = True + break + if found: + break + return ret + +def draw_line_3d(color, start, end): + bgl.glColor4f(*color) + bgl.glBegin(bgl.GL_LINES) + bgl.glVertex3f(*start) + bgl.glVertex3f(*end) + +def draw_callback_3d(self, context): + # object locations + if context.scene.hecl_path_obj not in context.scene.objects: + return + obj = context.scene.objects[context.scene.hecl_path_obj] + if obj.type != 'MESH': + return + heights = correlate_polygon_heights(context, obj) + obj_mtx = obj.matrix_world + + for p in obj.data.polygons: + height = 1.0 + if p in heights: + height = heights[p] + for ek in p.edge_keys: + co0 = obj_mtx * obj.data.vertices[ek[0]].co + co1 = obj_mtx * obj.data.vertices[ek[1]].co + draw_line_3d((0.0, 0.0, 1.0, 0.7), co0 + Vector((0.0, 0.0, height)), + co1 + Vector((0.0, 0.0, height))) + for vk in p.vertices: + co = obj_mtx * obj.data.vertices[vk].co + draw_line_3d((1.0, 0.0, 0.0, 0.7), co, co + Vector((0.0, 0.0, height))) + + bgl.glEnd() + # restore opengl defaults + bgl.glColor4f(0.0, 0.0, 0.0, 1.0) + +class PathHeightDrawOperator(bpy.types.Operator): + bl_idname = "view3d.toggle_path_height_visualization" + bl_label = "Toggle PATH height visualization" + _handle_3d = None + + def execute(self, context): + #heights = correlate_polygon_heights(context, bpy.data.objects['Plane']) + # the arguments we pass the the callback + args = (self, context) + # Add the region OpenGL drawing callback + # draw in view space with 'POST_VIEW' and 'PRE_VIEW' + if self._handle_3d is None: + PathHeightDrawOperator._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW') + else: + bpy.types.SpaceView3D.draw_handler_remove(PathHeightDrawOperator._handle_3d, 'WINDOW') + PathHeightDrawOperator._handle_3d = None + + for ar in bpy.context.screen.areas: + ar.tag_redraw() + return {'FINISHED'} + +class PathBackgroundWireframeOperator(bpy.types.Operator): + bl_idname = "view3d.toggle_path_background_wireframe" + bl_label = "Toggle PATH background wireframe" + _handle_3d = None + + def execute(self, context): + if context.scene.background_set: + to_wire = False + for o in context.scene.background_set.objects: + if o.draw_type != 'WIRE': + to_wire = True + break + if to_wire: + for o in context.scene.background_set.objects: + o.draw_type = 'WIRE' + else: + for o in context.scene.background_set.objects: + o.draw_type = 'TEXTURED' + return {'FINISHED'} + +# Registration +def register(): + bpy.types.Scene.hecl_path_obj = bpy.props.StringProperty( + name='HECL Path Object', + description='Blender Mesh Object to export during PATH\'s cook process') + bpy.utils.register_class(PathHeightDrawOperator) + bpy.utils.register_class(PathBackgroundWireframeOperator) + +def unregister(): + bpy.utils.unregister_class(PathHeightDrawOperator) + bpy.utils.unregister_class(PathBackgroundWireframeOperator) diff --git a/hecl/blender/hecl_blendershell.py b/hecl/blender/hecl_blendershell.py index e3db4c199..95fa1c371 100644 --- a/hecl/blender/hecl_blendershell.py +++ b/hecl/blender/hecl_blendershell.py @@ -267,6 +267,15 @@ def dataout_loop(): bpy.data.objects.remove(join_obj) bpy.data.meshes.remove(join_mesh) + elif cmdargs[0] == 'MESHCOMPILEPATH': + meshName = bpy.context.scene.hecl_path_obj + if meshName not in bpy.data.objects: + writepipestr(('mesh %s not found' % meshName).encode()) + continue + + writepipestr(b'OK') + hecl.path.cook(writepipebuf, bpy.data.objects[meshName]) + elif cmdargs[0] == 'WORLDCOMPILE': writepipestr(b'OK') hecl.swld.cook(writepipebuf) diff --git a/hecl/extern/athena b/hecl/extern/athena index 62b6d6792..17a0959db 160000 --- a/hecl/extern/athena +++ b/hecl/extern/athena @@ -1 +1 @@ -Subproject commit 62b6d6792a04792587f19d8fdf4c34d251fcfce0 +Subproject commit 17a0959dbd0a79f9f9630dc616adc79fff65ad80 diff --git a/hecl/include/hecl/Blender/Connection.hpp b/hecl/include/hecl/Blender/Connection.hpp index 67c72434f..6de7ef401 100644 --- a/hecl/include/hecl/Blender/Connection.hpp +++ b/hecl/include/hecl/Blender/Connection.hpp @@ -99,7 +99,7 @@ public: #endif void format(const char* fmt, ...); void linkBlend(const char* target, const char* objName, bool link=true); - void linkBackground(const char* target, const char* sceneName); + void linkBackground(const char* target, const char* sceneName=nullptr); void AABBToBMesh(const atVec3f& min, const atVec3f& max); void centerView(); @@ -509,6 +509,12 @@ struct Actor Actor(Connection& conn); }; +/** Intermediate pathfinding representation prepared by blender */ +struct PathMesh +{ + PathMesh(Connection& conn); +}; + class DataStream { friend class Connection; @@ -549,6 +555,9 @@ public: /** Gather all lights in scene (AREA blends only) */ std::vector compileLights(); + /** Get PathMesh from scene (PATH blends only) */ + PathMesh compilePathMesh(); + /** Compile GUI into FRME data (FRAME blends only) */ void compileGuiFrame(std::string_view pathOut, int version); diff --git a/hecl/include/hecl/hecl.hpp b/hecl/include/hecl/hecl.hpp index f00ce5732..7191e97cb 100644 --- a/hecl/include/hecl/hecl.hpp +++ b/hecl/include/hecl/hecl.hpp @@ -59,7 +59,8 @@ enum class BlendType World, MapArea, MapUniverse, - Frame + Frame, + PathMesh }; class Connection; @@ -78,6 +79,7 @@ struct Actor; struct Armature; struct Action; struct Bone; +struct PathMesh; struct Matrix3f; struct PoolSkinIndex; diff --git a/hecl/lib/Blender/Connection.cpp b/hecl/lib/Blender/Connection.cpp index a07b2ef68..af592c785 100644 --- a/hecl/lib/Blender/Connection.cpp +++ b/hecl/lib/Blender/Connection.cpp @@ -608,6 +608,7 @@ static const char* BlendTypeStrs[] = "MAPAREA", "MAPUNIVERSE", "FRAME", + "PATH", nullptr }; @@ -778,20 +779,37 @@ void PyOutStream::linkBlend(const char* target, const char* objName, bool link) void PyOutStream::linkBackground(const char* target, const char* sceneName) { - format("if '%s' not in bpy.data.scenes:\n" - " with bpy.data.libraries.load('''%s''', link=True, relative=True) as (data_from, data_to):\n" - " data_to.scenes = data_from.scenes\n" - " obj_scene = None\n" - " for scene in data_to.scenes:\n" - " if scene.name == '%s':\n" - " obj_scene = scene\n" - " break\n" - " if not obj_scene:\n" - " raise RuntimeError('''unable to find %s in %s. try deleting it and restart the extract.''')\n" - "\n" - "bpy.context.scene.background_set = bpy.data.scenes['%s']\n", - sceneName, target, - sceneName, sceneName, target, sceneName); + if (!sceneName) + { + format("with bpy.data.libraries.load('''%s''', link=True, relative=True) as (data_from, data_to):\n" + " data_to.scenes = data_from.scenes\n" + "obj_scene = None\n" + "for scene in data_to.scenes:\n" + " obj_scene = scene\n" + " break\n" + "if not obj_scene:\n" + " raise RuntimeError('''unable to find %s. try deleting it and restart the extract.''')\n" + "\n" + "bpy.context.scene.background_set = obj_scene\n", + target, target); + } + else + { + format("if '%s' not in bpy.data.scenes:\n" + " with bpy.data.libraries.load('''%s''', link=True, relative=True) as (data_from, data_to):\n" + " data_to.scenes = data_from.scenes\n" + " obj_scene = None\n" + " for scene in data_to.scenes:\n" + " if scene.name == '%s':\n" + " obj_scene = scene\n" + " break\n" + " if not obj_scene:\n" + " raise RuntimeError('''unable to find %s in %s. try deleting it and restart the extract.''')\n" + "\n" + "bpy.context.scene.background_set = bpy.data.scenes['%s']\n", + sceneName, target, + sceneName, sceneName, target, sceneName); + } } void PyOutStream::AABBToBMesh(const atVec3f& min, const atVec3f& max) @@ -1502,6 +1520,11 @@ Actor::Actor(Connection& conn) actions.emplace_back(conn); } +PathMesh::PathMesh(Connection& conn) +{ + +} + const Bone* Armature::lookupBone(const char* name) const { for (const Bone& b : bones) @@ -1895,6 +1918,22 @@ std::vector DataStream::compileLights() return ret; } +PathMesh DataStream::compilePathMesh() +{ + if (m_parent->getBlendType() != BlendType::PathMesh) + BlenderLog.report(logvisor::Fatal, _S("%s is not a PATH blend"), + m_parent->getBlendPath().getAbsolutePath().data()); + + m_parent->_writeStr("MESHCOMPILEPATH"); + + char readBuf[256]; + m_parent->_readStr(readBuf, 256); + if (strcmp(readBuf, "OK")) + BlenderLog.report(logvisor::Fatal, "unable to path collision mesh: %s", readBuf); + + return PathMesh(*m_parent); +} + void DataStream::compileGuiFrame(std::string_view pathOut, int version) { if (m_parent->getBlendType() != BlendType::Frame)