From aef455e1ab130cffb58da3b35b16a5deb8ca6386 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 11 Oct 2018 10:48:13 -1000 Subject: [PATCH] Attachment model support in blender addon --- hecl/blender/hecl/sact/SACTAction.py | 72 +++++++------ hecl/blender/hecl/sact/SACTSubtype.py | 129 +++++++++++++++++++++-- hecl/blender/hecl/sact/__init__.py | 46 ++++++++ hecl/blender/hecl_blendershell.py | 4 + hecl/extern/boo | 2 +- hecl/include/hecl/Backend/Backend.hpp | 42 ++++++++ hecl/include/hecl/Blender/Connection.hpp | 9 ++ hecl/include/hecl/Pipeline.hpp | 4 + hecl/include/hecl/PipelineBase.hpp | 4 +- hecl/include/hecl/hecl.hpp | 37 +------ hecl/lib/Blender/Connection.cpp | 60 +++++++++++ hecl/lib/ProjectPath.cpp | 34 +++++- 12 files changed, 362 insertions(+), 81 deletions(-) diff --git a/hecl/blender/hecl/sact/SACTAction.py b/hecl/blender/hecl/sact/SACTAction.py index 71df375d6..6a9ec6d3c 100644 --- a/hecl/blender/hecl/sact/SACTAction.py +++ b/hecl/blender/hecl/sact/SACTAction.py @@ -149,44 +149,50 @@ class SACTAction_load(bpy.types.Operator): # Set single action into armature if subtype.linked_armature in bpy.data.objects: - armature_obj = bpy.data.objects[subtype.linked_armature] + armature_objs = [bpy.data.objects[subtype.linked_armature]] + + for attachment in actor_data.attachments: + if attachment.linked_armature in bpy.data.objects: + attachment_armature = bpy.data.objects[attachment.linked_armature] + armature_objs.append(attachment_armature) - for bone in armature_obj.pose.bones: - bone.location = (0,0,0) - bone.rotation_quaternion = (1,0,0,0) - bone.scale = (1,1,1) + for armature_obj in armature_objs: + for bone in armature_obj.pose.bones: + bone.location = (0,0,0) + bone.rotation_quaternion = (1,0,0,0) + bone.scale = (1,1,1) - if action_data.name in bpy.data.actions: - action_obj =\ - bpy.data.actions[action_data.name] - armature_obj.animation_data_clear() - armature_obj.animation_data_create() - armature_obj.animation_data.action = action_obj + if action_data.name in bpy.data.actions: + action_obj =\ + bpy.data.actions[action_data.name] + armature_obj.animation_data_clear() + armature_obj.animation_data_create() + armature_obj.animation_data.action = action_obj + + # Time remapping + if context.scene.hecl_auto_remap: + bpy.context.scene.render.fps = 60 + bpy.context.scene.render.frame_map_old = action_obj.hecl_fps + bpy.context.scene.render.frame_map_new = 60 + bpy.context.scene.frame_start = 0 + bpy.context.scene.frame_end = action_obj.frame_range[1] * (60 / action_obj.hecl_fps) + else: + bpy.context.scene.render.fps = action_obj.hecl_fps + bpy.context.scene.render.frame_map_old = action_obj.hecl_fps + bpy.context.scene.render.frame_map_new = action_obj.hecl_fps + bpy.context.scene.frame_start = 0 + bpy.context.scene.frame_end = action_obj.frame_range[1] + + # Events + #SACTEvent.clear_action_events(self, context, actor_data) + #SACTEvent.load_action_events(self, context, action_obj, 0) - # Time remapping - if context.scene.hecl_auto_remap: - bpy.context.scene.render.fps = 60 - bpy.context.scene.render.frame_map_old = action_obj.hecl_fps - bpy.context.scene.render.frame_map_new = 60 - bpy.context.scene.frame_start = 0 - bpy.context.scene.frame_end = action_obj.frame_range[1] * (60 / action_obj.hecl_fps) else: - bpy.context.scene.render.fps = action_obj.hecl_fps - bpy.context.scene.render.frame_map_old = action_obj.hecl_fps - bpy.context.scene.render.frame_map_new = action_obj.hecl_fps - bpy.context.scene.frame_start = 0 - bpy.context.scene.frame_end = action_obj.frame_range[1] + armature_obj.animation_data_clear() + self.report({'WARNING'}, "Unable to load action; check HECL panel") + return {'FINISHED'} - # Events - #SACTEvent.clear_action_events(self, context, actor_data) - #SACTEvent.load_action_events(self, context, action_obj, 0) - - return {'FINISHED'} - - else: - armature_obj.animation_data_clear() - self.report({'WARNING'}, "Unable to load action; check HECL panel") - return {'FINISHED'} + return {'FINISHED'} else: self.report({'WARNING'}, "Unable to load armature; check HECL panel") diff --git a/hecl/blender/hecl/sact/SACTSubtype.py b/hecl/blender/hecl/sact/SACTSubtype.py index a004c4eb3..656427d3e 100644 --- a/hecl/blender/hecl/sact/SACTSubtype.py +++ b/hecl/blender/hecl/sact/SACTSubtype.py @@ -13,18 +13,25 @@ class SACTSubtypeOverlay(bpy.types.PropertyGroup): linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update) show_overlay = bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update) +# Actor attachment class +class SACTAttachment(bpy.types.PropertyGroup): + name = bpy.props.StringProperty(name="Attachment Name") + linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update) + linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update) + show_attachment = bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update) + # Actor subtype class class SACTSubtype(bpy.types.PropertyGroup): name = bpy.props.StringProperty(name="Actor Mesh Name") linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update) linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update) + show_mesh = bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update) overlays =\ bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List") active_overlay =\ bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update) - # Panel draw def draw(layout, context): actor_data = context.scene.hecl_sact_data @@ -70,6 +77,7 @@ def draw(layout, context): linked_mesh = None if subtype.linked_mesh in bpy.data.objects: linked_mesh = bpy.data.objects[subtype.linked_mesh] + layout.prop(subtype, 'show_mesh', text="Show Mesh") # Mesh overlays layout.label("Overlay Meshes:") @@ -84,12 +92,33 @@ def draw(layout, context): if len(subtype.overlays) and subtype.active_overlay >= 0: overlay = subtype.overlays[subtype.active_overlay] layout.prop(overlay, 'name', text="Name") - overlay = subtype.overlays[subtype.active_overlay] layout.prop_search(overlay, 'linked_mesh', bpy.data, 'objects', text="Mesh") if overlay.linked_mesh in bpy.data.objects: overlay_mesh = bpy.data.objects[overlay.linked_mesh] layout.prop(overlay, 'show_overlay', text="Show Overlay") + # Mesh attachments + layout.label("Attachment Meshes:") + row = layout.row() + row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments", + actor_data, 'attachments', actor_data, 'active_attachment') + col = row.column(align=True) + col.operator("scene.sactattachment_add", icon="ZOOMIN", text="") + col.operator("scene.sactattachment_remove", icon="ZOOMOUT", text="") + + attachment_armature = linked_armature + attachment_mesh = None + if len(actor_data.attachments) and actor_data.active_attachment >= 0: + attachment = actor_data.attachments[actor_data.active_attachment] + layout.prop(attachment, 'name', text="Name") + layout.prop_search(attachment, 'linked_armature', bpy.data, 'objects', text="Armature") + if attachment.linked_armature in bpy.data.objects: + attachment_armature = bpy.data.objects[attachment.linked_armature] + layout.prop_search(attachment, 'linked_mesh', bpy.data, 'objects', text="Mesh") + if attachment.linked_mesh in bpy.data.objects: + attachment_mesh = bpy.data.objects[attachment.linked_mesh] + layout.prop(attachment, 'show_attachment', text="Show Attachment") + # Validate if linked_mesh is None: layout.label("Source mesh not set", icon='ERROR') @@ -103,11 +132,17 @@ def draw(layout, context): if overlay_mesh: if overlay_mesh.type != 'MESH': layout.label("Overlay mesh not 'MESH'", icon='ERROR') - elif linked_armature is not None and overlay_mesh not in linked_armature.children: - layout.label(overlay_mesh.name+" not a child of "+linked_armature.name, icon='ERROR') elif overlay_mesh.parent_type != 'ARMATURE': layout.label("Overlay mesh not 'ARMATURE' parent type", icon='ERROR') + if attachment_mesh: + if attachment_mesh.type != 'MESH': + layout.label("Attachment mesh not 'MESH'", icon='ERROR') + elif attachment_armature is not None and attachment_mesh not in attachment_armature.children: + layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR') + elif attachment_mesh.parent_type != 'ARMATURE': + layout.label("Attachment mesh not 'ARMATURE' parent type", icon='ERROR') + # Subtype 'add' operator class SACTSubtype_add(bpy.types.Operator): @@ -190,28 +225,43 @@ class SACTSubtype_load(bpy.types.Operator): object.hide = True # Hide all meshes (incl overlays) - for mesh_name in actor_data.subtypes: - if mesh_name.linked_mesh in bpy.data.objects: - mesh = bpy.data.objects[mesh_name.linked_mesh] + for subtype_data in actor_data.subtypes: + if subtype_data.linked_mesh in bpy.data.objects: + mesh = bpy.data.objects[subtype_data.linked_mesh] if mesh.name in context.scene.objects: mesh.hide = True - for overlay in mesh_name.overlays: + for overlay in subtype_data.overlays: if overlay.linked_mesh in bpy.data.objects: mesh = bpy.data.objects[overlay.linked_mesh] if mesh.name in context.scene.objects: mesh.hide = True + # Hide/Show selected attachment meshes + for attachment in actor_data.attachments: + if attachment.linked_mesh in bpy.data.objects: + mesh_obj = bpy.data.objects[attachment.linked_mesh] + if mesh_obj.name in context.scene.objects: + mesh_obj.hide = not attachment.show_attachment + attachment_armature = linked_armature + if attachment.linked_armature in bpy.data.objects: + attachment_armature = bpy.data.objects[attachment.linked_armature] + if mesh_obj != attachment_armature: + mesh_obj.parent = attachment_armature + mesh_obj.parent_type = 'ARMATURE' + # Show only the chosen subtype (and selected overlays) if subtype.linked_mesh in bpy.data.objects: mesh_obj = bpy.data.objects[subtype.linked_mesh] - mesh_obj.hide = False + if subtype.show_mesh: + mesh_obj.hide = False if mesh_obj != linked_armature: mesh_obj.parent = linked_armature mesh_obj.parent_type = 'ARMATURE' for overlay in subtype.overlays: - if overlay.show_overlay and overlay.linked_mesh in bpy.data.objects: + if overlay.linked_mesh in bpy.data.objects: mesh_obj = bpy.data.objects[overlay.linked_mesh] - mesh_obj.hide = False + if overlay.show_overlay: + mesh_obj.hide = False if mesh_obj != linked_armature: mesh_obj.parent = linked_armature mesh_obj.parent_type = 'ARMATURE' @@ -275,11 +325,65 @@ class SACTSubtypeOverlay_remove(bpy.types.Operator): subtype.active_overlay = 0 return {'FINISHED'} +# Subtype overlay 'add' operator +class SACTAttachment_add(bpy.types.Operator): + bl_idname = "scene.sactattachment_add" + bl_label = "New HECL Actor Attachment" + bl_description = "Add New HECL Actor Attachment" + + @classmethod + def poll(cls, context): + actor_data = context.scene.hecl_sact_data + return (context.scene is not None and + not context.scene.library and + context.scene.hecl_type == 'ACTOR') + + def execute(self, context): + actor_data = context.scene.hecl_sact_data + attachment_name = 'ActorAttachment' + if attachment_name in actor_data.attachments: + attachment_name = 'ActorAttachment.001' + attachment_idx = 1 + while attachment_name in actor_data.attachments: + attachment_idx += 1 + attachment_name = 'ActorAttachment.{:0>3}'.format(attachment_idx) + attachment = actor_data.attachments.add() + attachment.name = attachment_name + actor_data.active_attachment = len(actor_data.attachments)-1 + + return {'FINISHED'} + +# Subtype overlay 'remove' operator +class SACTAttachment_remove(bpy.types.Operator): + bl_idname = "scene.sactattachment_remove" + bl_label = "Remove HECL Actor Attachment" + bl_description = "Remove HECL Actor Attachment" + + @classmethod + def poll(cls, context): + actor_data = context.scene.hecl_sact_data + return (context.scene is not None and + not context.scene.library and + context.scene.hecl_type == 'ACTOR' and + actor_data.active_attachment >= 0 and + len(actor_data.attachments)) + + def execute(self, context): + actor_data = context.scene.hecl_sact_data + actor_data.attachments.remove(actor_data.active_attachment) + actor_data.active_attachment -= 1 + if actor_data.active_attachment == -1: + actor_data.active_attachment = 0 + return {'FINISHED'} + # Registration def register(): bpy.utils.register_class(SACTSubtypeOverlay) bpy.utils.register_class(SACTSubtypeOverlay_add) bpy.utils.register_class(SACTSubtypeOverlay_remove) + bpy.utils.register_class(SACTAttachment) + bpy.utils.register_class(SACTAttachment_add) + bpy.utils.register_class(SACTAttachment_remove) bpy.utils.register_class(SACTSubtype) bpy.utils.register_class(SACTSubtype_add) bpy.utils.register_class(SACTSubtype_remove) @@ -290,6 +394,9 @@ def unregister(): bpy.utils.unregister_class(SACTSubtype_add) bpy.utils.unregister_class(SACTSubtype_remove) bpy.utils.unregister_class(SACTSubtype_load) + bpy.utils.unregister_class(SACTAttachment) + bpy.utils.unregister_class(SACTAttachment_add) + bpy.utils.unregister_class(SACTAttachment_remove) bpy.utils.unregister_class(SACTSubtypeOverlay) bpy.utils.unregister_class(SACTSubtypeOverlay_add) bpy.utils.unregister_class(SACTSubtypeOverlay_remove) diff --git a/hecl/blender/hecl/sact/__init__.py b/hecl/blender/hecl/sact/__init__.py index f144c979f..b65c0b5bd 100644 --- a/hecl/blender/hecl/sact/__init__.py +++ b/hecl/blender/hecl/sact/__init__.py @@ -19,6 +19,11 @@ class SACTData(bpy.types.PropertyGroup): show_subtypes =\ bpy.props.BoolProperty() + attachments = \ + bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List") + active_attachment = \ + bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update) + actions =\ bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List") active_action =\ @@ -268,6 +273,32 @@ def _out_subtypes(sact_data, writebuf): else: writebuf(struct.pack('I', 0)) +def _out_attachments(sact_data, writebuf): + writebuf(struct.pack('I', len(sact_data.attachments))) + for attachment in sact_data.attachments: + writebuf(struct.pack('I', len(attachment.name))) + writebuf(attachment.name.encode()) + + mesh = None + if attachment.linked_mesh in bpy.data.objects: + mesh = bpy.data.objects[attachment.linked_mesh] + + if mesh and mesh.library: + mesh_path = bpy.path.abspath(mesh.library.filepath) + writebuf(struct.pack('I', len(mesh_path))) + writebuf(mesh_path.encode()) + else: + writebuf(struct.pack('I', 0)) + + arm = None + if attachment.linked_armature in bpy.data.objects: + arm = bpy.data.objects[attachment.linked_armature] + + arm_idx = -1 + if arm: + arm_idx = bpy.data.armatures.find(arm.name) + writebuf(struct.pack('i', arm_idx)) + def _out_actions(sact_data, writebuf): writebuf(struct.pack('I', len(sact_data.actions))) for action_idx in range(len(sact_data.actions)): @@ -335,6 +366,9 @@ def cook(writebuf): # Output subtypes _out_subtypes(sact_data, writebuf) + # Output attachments + _out_attachments(sact_data, writebuf) + # Output actions _out_actions(sact_data, writebuf) @@ -348,6 +382,9 @@ def cook_character_only(writebuf): # Output subtypes _out_subtypes(sact_data, writebuf) + # Output attachments + _out_attachments(sact_data, writebuf) + # Output no actions writebuf(struct.pack('I', 0)) @@ -387,6 +424,15 @@ def get_subtype_overlay_names(writebuf, subtypeName): return writebuf(struct.pack('I', 0)) +# Access contained attachment names +def get_attachment_names(writebuf): + sact_data = bpy.context.scene.hecl_sact_data + writebuf(struct.pack('I', len(sact_data.attachments))) + for att_idx in range(len(sact_data.attachments)): + attachment = sact_data.attachments[att_idx] + writebuf(struct.pack('I', len(attachment.name))) + writebuf(attachment.name.encode()) + # Access actor's contained action names def get_action_names(writebuf): sact_data = bpy.context.scene.hecl_sact_data diff --git a/hecl/blender/hecl_blendershell.py b/hecl/blender/hecl_blendershell.py index 902c6ab0d..f548e8ca9 100644 --- a/hecl/blender/hecl_blendershell.py +++ b/hecl/blender/hecl_blendershell.py @@ -419,6 +419,10 @@ def dataout_loop(): writepipestr(b'OK') hecl.sact.get_subtype_overlay_names(writepipebuf, subtypeName) + elif cmdargs[0] == 'GETATTACHMENTNAMES': + writepipestr(b'OK') + hecl.sact.get_attachment_names(writepipebuf) + elif cmdargs[0] == 'GETACTIONNAMES': writepipestr(b'OK') hecl.sact.get_action_names(writepipebuf) diff --git a/hecl/extern/boo b/hecl/extern/boo index bba2486c1..f917d154b 160000 --- a/hecl/extern/boo +++ b/hecl/extern/boo @@ -1 +1 @@ -Subproject commit bba2486c15498a307eb1c009edba09ec10a10294 +Subproject commit f917d154b2ff38a5cbeea8c536c91e7813be060d diff --git a/hecl/include/hecl/Backend/Backend.hpp b/hecl/include/hecl/Backend/Backend.hpp index d0323191a..45c1cfcef 100644 --- a/hecl/include/hecl/Backend/Backend.hpp +++ b/hecl/include/hecl/Backend/Backend.hpp @@ -4,6 +4,7 @@ namespace hecl::Backend { +struct ExtensionSlot; using IR = Frontend::IR; using Diagnostics = Frontend::Diagnostics; @@ -159,6 +160,9 @@ public: return ret; } + + boo::AdditionalPipelineInfo additionalInfo(const ExtensionSlot& ext, + std::pair blendFactors) const; }; struct Function @@ -226,6 +230,44 @@ struct ExtensionSlot } }; +inline boo::AdditionalPipelineInfo ShaderTag::additionalInfo(const ExtensionSlot& ext, + std::pair blendFactors) const +{ + boo::ZTest zTest; + switch (ext.depthTest) + { + case hecl::Backend::ZTest::Original: + default: + zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; + break; + case hecl::Backend::ZTest::None: + zTest = boo::ZTest::None; + break; + case hecl::Backend::ZTest::LEqual: + zTest = boo::ZTest::LEqual; + break; + case hecl::Backend::ZTest::Greater: + zTest = boo::ZTest::Greater; + break; + case hecl::Backend::ZTest::Equal: + zTest = boo::ZTest::Equal; + break; + case hecl::Backend::ZTest::GEqual: + zTest = boo::ZTest::GEqual; + break; + } + + return { + boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? blendFactors.first : ext.srcFactor), + boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? blendFactors.second : ext.dstFactor), + getPrimType(), zTest, ext.noDepthWrite ? false : getDepthWrite(), + !ext.noColorWrite, !ext.noAlphaWrite, + (ext.cullMode == hecl::Backend::CullMode::Original) ? + (getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : + boo::CullMode(ext.cullMode), !ext.noAlphaOverwrite + }; +} + } namespace std diff --git a/hecl/include/hecl/Blender/Connection.hpp b/hecl/include/hecl/Blender/Connection.hpp index 48345a32f..43a7af070 100644 --- a/hecl/include/hecl/Blender/Connection.hpp +++ b/hecl/include/hecl/Blender/Connection.hpp @@ -504,6 +504,14 @@ struct Actor Subtype(Connection& conn); }; std::vector subtypes; + struct Attachment + { + std::string name; + ProjectPath mesh; + int32_t armature = -1; + Attachment(Connection& conn); + }; + std::vector attachments; std::vector actions; Actor(Connection& conn); @@ -572,6 +580,7 @@ public: std::vector getSubtypeNames(); std::vector getActionNames(); std::vector getSubtypeOverlayNames(std::string_view name); + std::vector getAttachmentNames(); std::unordered_map getBoneMatrices(std::string_view name); diff --git a/hecl/include/hecl/Pipeline.hpp b/hecl/include/hecl/Pipeline.hpp index 564b020bf..a9dac6140 100644 --- a/hecl/include/hecl/Pipeline.hpp +++ b/hecl/include/hecl/Pipeline.hpp @@ -83,6 +83,9 @@ public: m_extension.texCount, m_extension.texs); } const hecl::Backend::ShaderTag& getTag() const { return m_tag; } + const hecl::Backend::ExtensionSlot& extension() const { return m_extension; } + std::pair blendFactors() const + { return {m_backend.m_blendSrc, m_backend.m_blendDst}; } }; template @@ -134,6 +137,7 @@ StageCollection>::StageCollection(PipelineConverter

& conv, Fact m_fragment = conv.getFragmentConverter().convert(ctx, StageSourceText(in.makeFrag())); m_vtxFmtData = in.getTag().vertexFormat(); m_vtxFmt = boo::VertexFormatInfo(m_vtxFmtData.size(), m_vtxFmtData.data()); + m_additionalInfo = in.getTag().additionalInfo(in.extension(), in.blendFactors()); MakeHash(); } diff --git a/hecl/include/hecl/PipelineBase.hpp b/hecl/include/hecl/PipelineBase.hpp index 6700e4329..a1c6b7d4c 100644 --- a/hecl/include/hecl/PipelineBase.hpp +++ b/hecl/include/hecl/PipelineBase.hpp @@ -66,9 +66,7 @@ public: uint64_t Hash() const { return m_hash; } explicit StageSourceText(std::string_view text) - { - m_hash = XXH64(m_text.data(), m_text.size(), 0); - } + : m_text(text), m_hash(XXH64(m_text.data(), m_text.size(), 0)) {} std::string_view text() const { return m_text; } }; diff --git a/hecl/include/hecl/hecl.hpp b/hecl/include/hecl/hecl.hpp index df4ef2792..70440d741 100644 --- a/hecl/include/hecl/hecl.hpp +++ b/hecl/include/hecl/hecl.hpp @@ -834,37 +834,7 @@ public: * @param replace remove existing extension (if any) before appending new extension * @return new path with extension */ - ProjectPath getWithExtension(const SystemChar* ext, bool replace=false) const - { - ProjectPath pp(*this); - if (replace) - { - auto relIt = pp.m_relPath.end(); - if (relIt != pp.m_relPath.begin()) - --relIt; - auto absIt = pp.m_absPath.end(); - if (absIt != pp.m_absPath.begin()) - --absIt; - while (relIt != pp.m_relPath.begin() && *relIt != _S('.') && *relIt != _S('/')) - { - --relIt; - --absIt; - } - if (*relIt == _S('.') && relIt != pp.m_relPath.begin()) - { - pp.m_relPath.resize(relIt - pp.m_relPath.begin()); - pp.m_absPath.resize(absIt - pp.m_absPath.begin()); - } - } - if (ext) - { - pp.m_relPath += ext; - pp.m_absPath += ext; - } - - pp.ComputeHash(); - return pp; - } + ProjectPath getWithExtension(const SystemChar* ext, bool replace=false) const; /** * @brief Access fully-canonicalized absolute path @@ -1063,7 +1033,10 @@ public: */ ProjectPath ensureAuxInfo(SystemStringView auxStr) const { - return ProjectPath(getProject(), SystemString(getRelativePath()) + _S('|') + auxStr.data()); + if (auxStr.empty()) + return ProjectPath(getProject(), getRelativePath()); + else + return ProjectPath(getProject(), SystemString(getRelativePath()) + _S('|') + auxStr.data()); } #if HECL_UCS2 diff --git a/hecl/lib/Blender/Connection.cpp b/hecl/lib/Blender/Connection.cpp index 6be7f88fc..1ed6374b5 100644 --- a/hecl/lib/Blender/Connection.cpp +++ b/hecl/lib/Blender/Connection.cpp @@ -1546,6 +1546,12 @@ Actor::Actor(Connection& conn) for (uint32_t i=0 ; i DataStream::getSubtypeOverlayNames(std::string_view nam return ret; } +std::vector DataStream::getAttachmentNames() +{ + if (m_parent->getBlendType() != BlendType::Actor) + BlenderLog.report(logvisor::Fatal, _S("%s is not an ACTOR blend"), + m_parent->getBlendPath().getAbsolutePath().data()); + + m_parent->_writeStr("GETATTACHMENTNAMES"); + + char readBuf[256]; + m_parent->_readStr(readBuf, 256); + if (strcmp(readBuf, "OK")) + BlenderLog.report(logvisor::Fatal, "unable to get attachments of actor: %s", readBuf); + + std::vector ret; + + uint32_t attCount; + m_parent->_readBuf(&attCount, 4); + ret.reserve(attCount); + for (uint32_t i=0 ; i_readBuf(&bufSz, 4); + name.assign(bufSz, ' '); + m_parent->_readBuf(&name[0], bufSz); + } + + return ret; +} + std::unordered_map DataStream::getBoneMatrices(std::string_view name) { diff --git a/hecl/lib/ProjectPath.cpp b/hecl/lib/ProjectPath.cpp index 9f2e3bc23..f59a8acfd 100644 --- a/hecl/lib/ProjectPath.cpp +++ b/hecl/lib/ProjectPath.cpp @@ -77,7 +77,7 @@ void ProjectPath::assign(Database::Project& project, SystemStringView path) m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _S('/') + m_relPath; SanitizePath(m_relPath); SanitizePath(m_absPath); - + ComputeHash(); } @@ -119,6 +119,38 @@ void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path) } #endif +ProjectPath ProjectPath::getWithExtension(const SystemChar* ext, bool replace) const +{ + ProjectPath pp(*this); + if (replace) + { + auto relIt = pp.m_relPath.end(); + if (relIt != pp.m_relPath.begin()) + --relIt; + auto absIt = pp.m_absPath.end(); + if (absIt != pp.m_absPath.begin()) + --absIt; + while (relIt != pp.m_relPath.begin() && *relIt != _S('.') && *relIt != _S('/')) + { + --relIt; + --absIt; + } + if (*relIt == _S('.') && relIt != pp.m_relPath.begin()) + { + pp.m_relPath.resize(relIt - pp.m_relPath.begin()); + pp.m_absPath.resize(absIt - pp.m_absPath.begin()); + } + } + if (ext) + { + pp.m_relPath += ext; + pp.m_absPath += ext; + } + + pp.ComputeHash(); + return pp; +} + ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const { ProjectPath woExt = getWithExtension(nullptr, true);