initial actor cooking support

This commit is contained in:
Jack Andersen 2015-10-22 14:44:37 -10:00
parent bcee8aa897
commit fc64c4d4a4
12 changed files with 586 additions and 851 deletions

View File

@ -587,6 +587,7 @@ BlenderConnection::DataStream::Mesh::getContiguousSkinningVersion() const
Mesh newMesh = *this;
newMesh.pos.clear();
newMesh.norm.clear();
newMesh.contiguousSkinVertCounts.clear();
newMesh.contiguousSkinVertCounts.reserve(skins.size());
for (size_t i=0 ; i<skins.size() ; ++i)
{
@ -754,6 +755,174 @@ uint32_t BlenderConnection::DataStream::Mesh::SkinBanks::addSurface
return uint32_t(-1);
}
BlenderConnection::DataStream::Actor::Actor(BlenderConnection& conn)
{
uint32_t armCount;
conn._readBuf(&armCount, 4);
armatures.reserve(armCount);
for (uint32_t i=0 ; i<armCount ; ++i)
armatures.emplace_back(conn);
uint32_t subtypeCount;
conn._readBuf(&subtypeCount, 4);
subtypes.reserve(subtypeCount);
for (uint32_t i=0 ; i<subtypeCount ; ++i)
subtypes.emplace_back(conn);
uint32_t actionCount;
conn._readBuf(&actionCount, 4);
actions.reserve(actionCount);
for (uint32_t i=0 ; i<actionCount ; ++i)
actions.emplace_back(conn);
}
BlenderConnection::DataStream::Actor::Armature::Armature(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
uint32_t boneCount;
conn._readBuf(&boneCount, 4);
bones.reserve(boneCount);
for (uint32_t i=0 ; i<boneCount ; ++i)
bones.emplace_back(conn);
}
BlenderConnection::DataStream::Actor::Armature::Bone::Bone(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
origin.read(conn);
conn._readBuf(&parent, 4);
uint32_t childCount;
conn._readBuf(&childCount, 4);
children.reserve(childCount);
for (uint32_t i=0 ; i<childCount ; ++i)
{
children.emplace_back(0);
conn._readBuf(&children.back(), 4);
}
}
BlenderConnection::DataStream::Actor::Subtype::Subtype(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
std::string meshPath;
conn._readBuf(&bufSz, 4);
if (bufSz)
{
meshPath.assign(bufSz, ' ');
conn._readBuf(&meshPath[0], bufSz);
SystemStringView meshPathAbs(meshPath);
SystemString meshPathRel =
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(meshPathAbs);
mesh.assign(conn.m_loadedBlend.getProject().getProjectWorkingPath(), meshPathRel);
}
conn._readBuf(&armature, 4);
uint32_t overlayCount;
conn._readBuf(&overlayCount, 4);
overlayMeshes.reserve(overlayCount);
for (uint32_t i=0 ; i<overlayCount ; ++i)
{
std::string overlayName;
conn._readBuf(&bufSz, 4);
overlayName.assign(bufSz, ' ');
conn._readBuf(&overlayName[0], bufSz);
std::string meshPath;
conn._readBuf(&bufSz, 4);
if (bufSz)
{
meshPath.assign(bufSz, ' ');
conn._readBuf(&meshPath[0], bufSz);
SystemStringView meshPathAbs(meshPath);
SystemString meshPathRel =
conn.m_loadedBlend.getProject().getProjectRootPath().getProjectRelativeFromAbsolute(meshPathAbs);
overlayMeshes.emplace_back(std::move(overlayName),
ProjectPath(conn.m_loadedBlend.getProject().getProjectWorkingPath(), meshPathRel));
}
}
}
BlenderConnection::DataStream::Actor::Action::Action(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
name.assign(bufSz, ' ');
conn._readBuf(&name[0], bufSz);
conn._readBuf(&interval, 4);
conn._readBuf(&additive, 1);
uint32_t frameCount;
conn._readBuf(&frameCount, 4);
frames.reserve(frameCount);
for (uint32_t i=0 ; i<frameCount ; ++i)
{
frames.emplace_back();
conn._readBuf(&frames.back(), 4);
}
uint32_t chanCount;
conn._readBuf(&chanCount, 4);
channels.reserve(chanCount);
for (uint32_t i=0 ; i<chanCount ; ++i)
channels.emplace_back(conn);
uint32_t aabbCount;
conn._readBuf(&aabbCount, 4);
subtypeAABBs.reserve(aabbCount);
for (uint32_t i=0 ; i<aabbCount ; ++i)
{
subtypeAABBs.emplace_back();
subtypeAABBs.back().first.read(conn);
subtypeAABBs.back().second.read(conn);
}
}
BlenderConnection::DataStream::Actor::Action::Channel::Channel(BlenderConnection& conn)
{
uint32_t bufSz;
conn._readBuf(&bufSz, 4);
boneName.assign(bufSz, ' ');
conn._readBuf(&boneName[0], bufSz);
conn._readBuf(&attrMask, 4);
uint32_t keyCount;
conn._readBuf(&keyCount, 4);
keys.reserve(keyCount);
for (uint32_t i=0 ; i<keyCount ; ++i)
keys.emplace_back(conn, attrMask);
}
BlenderConnection::DataStream::Actor::Action::Channel::Key::Key(BlenderConnection& conn, uint32_t attrMask)
{
if (attrMask & 1)
rotation.read(conn);
if (attrMask & 2)
position.read(conn);
if (attrMask & 4)
scale.read(conn);
}
void BlenderConnection::quitBlender()
{
_writeLine("QUIT");

View File

@ -66,6 +66,13 @@ public:
bool saveBlend();
void deleteBlend();
enum ANIMCurveType
{
CurveRotate,
CurveTranslate,
CurveScale
};
class PyOutStream : public std::ostream
{
friend class BlenderConnection;
@ -165,12 +172,7 @@ public:
unsigned m_totalCount = 0;
bool m_inCurve = false;
public:
enum CurveType
{
CurveRotate,
CurveTranslate,
CurveScale
};
using CurveType = ANIMCurveType;
ANIMOutStream(BlenderConnection* parent)
: m_parent(parent)
{
@ -283,28 +285,43 @@ public:
return retval;
}
/* Intermediate mesh representation prepared by blender from a single mesh object */
struct Mesh
{
/* Vector types with integrated stream reading constructor */
struct Vector2f
{
atVec2f val;
Vector2f(BlenderConnection& conn) {conn._readBuf(&val, 8);}
Vector2f() = default;
void read(BlenderConnection& conn) {conn._readBuf(&val, 8);}
Vector2f(BlenderConnection& conn) {read(conn);}
operator const atVec2f&() const {return val;}
};
struct Vector3f
{
atVec3f val;
Vector3f(BlenderConnection& conn) {conn._readBuf(&val, 12);}
Vector3f() = default;
void read(BlenderConnection& conn) {conn._readBuf(&val, 12);}
Vector3f(BlenderConnection& conn) {read(conn);}
operator const atVec3f&() const {return val;}
};
struct Vector4f
{
atVec4f val;
Vector4f() = default;
void read(BlenderConnection& conn) {conn._readBuf(&val, 16);}
Vector4f(BlenderConnection& conn) {read(conn);}
operator const atVec4f&() const {return val;}
};
struct Index
{
uint32_t val;
Index(BlenderConnection& conn) {conn._readBuf(&val, 4);}
Index() = default;
void read(BlenderConnection& conn) {conn._readBuf(&val, 4);}
Index(BlenderConnection& conn) {read(conn);}
operator const uint32_t&() const {return val;}
};
/** Intermediate mesh representation prepared by blender from a single mesh object */
struct Mesh
{
enum OutputMode
{
OutputTriangles,
@ -401,10 +418,14 @@ public:
}
/* Compile mesh by context */
/** Compile mesh by context (MESH blends only) */
Mesh compileMesh(Mesh::OutputMode outMode, int skinSlotCount=10,
Mesh::SurfProgFunc surfProg=[](int){})
{
if (m_parent->m_loadedType != TypeMesh)
BlenderLog.report(LogVisor::FatalError, _S("%s is not a MESH blend"),
m_parent->m_loadedBlend.getAbsolutePath().c_str());
char req[128];
snprintf(req, 128, "MESHCOMPILE %s %d",
MeshOutputModeString(outMode), skinSlotCount);
@ -418,10 +439,14 @@ public:
return Mesh(*m_parent, outMode, skinSlotCount, surfProg);
}
/* Compile mesh by name */
/** Compile mesh by name (AREA blends only) */
Mesh compileMesh(const std::string& name, Mesh::OutputMode outMode, int skinSlotCount=10,
Mesh::SurfProgFunc surfProg=[](int){})
{
if (m_parent->m_loadedType != TypeArea)
BlenderLog.report(LogVisor::FatalError, _S("%s is not an AREA blend"),
m_parent->m_loadedBlend.getAbsolutePath().c_str());
char req[128];
snprintf(req, 128, "MESHCOMPILENAME %s %s %d", name.c_str(),
MeshOutputModeString(outMode), skinSlotCount);
@ -435,10 +460,14 @@ public:
return Mesh(*m_parent, outMode, skinSlotCount, surfProg);
}
/* Compile all meshes into one */
/** Compile all meshes into one (AREA blends only) */
Mesh compileAllMeshes(Mesh::OutputMode outMode, int skinSlotCount=10, float maxOctantLength=5.0,
Mesh::SurfProgFunc surfProg=[](int){})
{
if (m_parent->m_loadedType != TypeArea)
BlenderLog.report(LogVisor::FatalError, _S("%s is not an AREA blend"),
m_parent->m_loadedBlend.getAbsolutePath().c_str());
char req[128];
snprintf(req, 128, "MESHCOMPILEALL %s %d %f",
MeshOutputModeString(outMode),
@ -452,6 +481,87 @@ public:
return Mesh(*m_parent, outMode, skinSlotCount, surfProg);
}
/** Intermediate actor representation prepared by blender from a single HECL actor blend */
struct Actor
{
struct Armature
{
std::string name;
struct Bone
{
std::string name;
Vector3f origin;
int32_t parent = -1;
std::vector<int32_t> children;
Bone(BlenderConnection& conn);
};
std::vector<Bone> bones;
Bone* lookupBone(const char* name)
{
for (Bone& b : bones)
if (!b.name.compare(name))
return &b;
return nullptr;
}
Armature(BlenderConnection& conn);
};
std::vector<Armature> armatures;
struct Subtype
{
std::string name;
ProjectPath mesh;
int32_t armature = -1;
std::vector<std::pair<std::string, ProjectPath>> overlayMeshes;
Subtype(BlenderConnection& conn);
};
std::vector<Subtype> subtypes;
struct Action
{
std::string name;
float interval;
bool additive;
std::vector<int32_t> frames;
struct Channel
{
std::string boneName;
uint32_t attrMask;
struct Key
{
Vector4f rotation;
Vector3f position;
Vector3f scale;
Key(BlenderConnection& conn, uint32_t attrMask);
};
std::vector<Key> keys;
Channel(BlenderConnection& conn);
};
std::vector<Channel> channels;
std::vector<std::pair<Vector3f, Vector3f>> subtypeAABBs;
Action(BlenderConnection& conn);
};
std::vector<Action> actions;
Actor(BlenderConnection& conn);
};
Actor compileActor()
{
if (m_parent->m_loadedType != TypeActor)
BlenderLog.report(LogVisor::FatalError, _S("%s is not an ACTOR blend"),
m_parent->m_loadedBlend.getAbsolutePath().c_str());
m_parent->_writeLine("ACTORCOMPILE");
char readBuf[256];
m_parent->_readLine(readBuf, 256);
if (strcmp(readBuf, "OK"))
BlenderLog.report(LogVisor::FatalError, "unable to compile actor: %s", readBuf);
return Actor(*m_parent);
}
};
DataStream beginData()
{

View File

@ -5,10 +5,8 @@ list(APPEND PY_SOURCES
hecl/hmdl/__init__.py
hecl/hmdl/HMDLMesh.py
hecl/hmdl/HMDLShader.py
hecl/hmdl/HMDLSkin.py
hecl/sact/__init__.py
hecl/sact/SACTAction.py
hecl/sact/SACTEvent.py
hecl/sact/SACTSubtype.py
hecl/srea/__init__.py)

View File

@ -1,11 +1,9 @@
'''Root HECL addon package for Blender'''
bl_info = {
"name": "HECL",
"author": "Jack Andersen <jackoalan@gmail.com>",
"version": (1, 0),
"blender": (2, 75),
"tracker_url": "https://github.com/RetroView/hecl/issues/new",
"blender": (2, 76),
"tracker_url": "https://github.com/AxioDL/hecl/issues/new",
"location": "Properties > Scene > HECL",
"description": "Enables blender to gather meshes, materials, and textures for hecl",
"category": "System"}

View File

@ -1,8 +1,3 @@
'''
HMDL Export Blender Addon
By Jack Andersen <jackoalan@gmail.com>
'''
import bpy, bmesh, operator, struct
from mathutils import Vector

View File

@ -1,11 +1,3 @@
'''
HMDL Export Blender Addon
By Jack Andersen <jackoalan@gmail.com>
Traces the 'Blender Internal' shader node structure to generate a
HECL combiner string
'''
import bpy, bpy.path, os.path
def get_texmap_idx(tex_list, name):

View File

@ -1,97 +0,0 @@
'''
HMDL Export Blender Addon
By Jack Andersen <jackoalan@gmail.com>
This file defines the `hmdl_skin` class to iteratively construct
a Skinning Info Section for HMDL files. Used by draw-format
generators to select an optimal skin entry for a draw primitive,
or have a new one established.
'''
import struct
import bpy
class hmdl_skin:
# Set up with HMDL containing ARMATURE `object`
def __init__(self, max_bone_count, vertex_groups):
self.max_bone_count = max_bone_count
self.mesh_vertex_groups = vertex_groups
self.bone_arrays = []
# Augment bone array with loop vert and return weight array
# Returns 'None' if bone overflow
def augment_bone_array_with_lv(self, mesh_data, bone_array, loop_vert):
vertex = mesh_data.vertices[loop_vert[0].loop.vertex_index]
# Loop-vert weight array
weight_array = []
for i in range(len(bone_array)):
weight_array.append(0.0)
# Tentative bone additions
new_bones = []
# Determine which bones (vertex groups) belong to loop_vert
for group_elem in vertex.groups:
vertex_group = self.mesh_vertex_groups[group_elem.group]
if vertex_group.name not in bone_array:
# Detect bone overflow
if len(bone_array) + len(new_bones) >= self.max_bone_count:
return None
# Add to array otherwise
new_bones.append(vertex_group.name)
# Record bone weight
weight_array.append(group_elem.weight)
else:
# Record bone weight
weight_array[bone_array.index(vertex_group.name)] = group_elem.weight
# If we get here, no overflows; augment bone array and return weight array
bone_array.extend(new_bones)
return weight_array
# Augment triangle-strip bone array to rigging info
def augment_skin(self, bone_array):
if bone_array not in self.bone_arrays:
self.bone_arrays.append(bone_array)
return (len(self.bone_arrays)-1)
return self.bone_arrays.index(bone_array)
# Generate Rigging Info structure (call after all index-buffers generated)
def generate_rigging_info(self, bone_dict, endian_char):
skin_entries = []
for bone_array in self.bone_arrays:
skin_bytes = bytearray()
skin_bytes += struct.pack(endian_char + 'I', len(bone_array))
for bone in bone_array:
skin_bytes += struct.pack(endian_char + 'I', bone_dict[bone])
skin_entries.append(skin_bytes)
# Generate skinning data
info_bytes = bytearray()
info_bytes += struct.pack(endian_char + 'I', len(skin_entries))
cur_offset = len(skin_entries) * 4 + 4
for entry in skin_entries:
info_bytes += struct.pack(endian_char + 'I', cur_offset)
cur_offset += len(entry)
for entry in skin_entries:
info_bytes += entry
return info_bytes

View File

@ -1,69 +1,6 @@
'''
HMDL Export Blender Addon
By Jack Andersen <jackoalan@gmail.com>
This Python module provides a generator implementation for
the 'HMDL' mesh format designed for use with HECL.
The format features three main sections:
* Shader table
* Skin-binding table
* Mesh table (VBOs [array,element], VAO attribs, drawing index)
The Shader table provides index-referenced binding points
for mesh-portions to use for rendering.
The Skin-binding table provides the runtime with identifiers
to use in ensuring the correct bone-transformations are bound
to the shader when rendering a specific primitive.
The Mesh table contains Vertex and Element buffers with interleaved
Positions, Normals, UV coordinates, and Weight Vectors
'''
import struct, bpy, bmesh
from mathutils import Vector
from . import HMDLShader, HMDLSkin, HMDLMesh
# Generate Skeleton Info structure (free-form tree structure)
def generate_skeleton_info(armature, endian_char='<'):
bones = []
for bone in armature.data.bones:
bone_bytes = bytearray()
# Write bone hash
bone_bytes += struct.pack(endian_char + 'I', hmdl_anim.hashbone(bone.name))
for comp in bone.head_local:
bone_bytes += struct.pack(endian_char + 'f', comp)
parent_idx = -1
if bone.parent:
parent_idx = armature.data.bones.find(bone.parent.name)
bone_bytes += struct.pack(endian_char + 'i', parent_idx)
bone_bytes += struct.pack(endian_char + 'I', len(bone.children))
for child in bone.children:
child_idx = armature.data.bones.find(child.name)
bone_bytes += struct.pack(endian_char + 'I', child_idx)
bones.append(bone_bytes)
# Generate bone tree data
info_bytes = bytearray()
info_bytes += struct.pack(endian_char + 'I', len(bones))
cur_offset = len(bones) * 4 + 4
for bone in bones:
info_bytes += struct.pack(endian_char + 'I', cur_offset)
cur_offset += len(bone)
for bone in bones:
info_bytes += bone
return info_bytes
from . import HMDLShader, HMDLMesh
def write_out_material(writebuf, mat, mesh_obj):
hecl_str, texs = HMDLShader.shader(mat, mesh_obj)

View File

@ -1,442 +0,0 @@
import bpy
# Loop event class
class hecl_actor_event_loop(bpy.types.PropertyGroup):
bool = bpy.props.BoolProperty(name="Loop Bool")
# UEVT event class
class hecl_actor_event_uevt(bpy.types.PropertyGroup):
type = bpy.props.IntProperty(name="UEVT Type")
bone_name = bpy.props.StringProperty(name="Bone Name")
# Effect event class
class hecl_actor_event_effect(bpy.types.PropertyGroup):
frame_count = bpy.props.IntProperty(name="Frame Count", min=0)
uid = bpy.props.StringProperty(name="Effect UID")
bone_name = bpy.props.StringProperty(name="Bone Name")
scale = bpy.props.FloatProperty(name="Scale", description="Proportional spacial scale")
transform_mode = bpy.props.EnumProperty(name="Transform Mode", description="How the bone will transform the effect",
items=[('STATIONARY', "Stationary", "Effect emitter will be transformed in bone-space, then retained"),
('WORLD', "World", "Effect emitter will be transformed in bone-space"),
('LOCAL', "Local", "Entire effect will be transformed in bone-space")])
# Sound event class
class hecl_actor_event_sound(bpy.types.PropertyGroup):
sound_id = bpy.props.StringProperty(name="Sound ID")
ref_amp = bpy.props.FloatProperty(name="Reference Amplitude")
ref_dist = bpy.props.FloatProperty(name="Reference Distance")
# Name update
def update_name(self, context):
if bpy.context.scene.hecl_type == 'ACTOR':
clear_event_markers(bpy.context.scene.hecl_sact_data, context)
update_action_events(None)
active_event_update(self, context)
# Actor event class
class hecl_actor_event(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Event Name",
update=update_name)
type = bpy.props.EnumProperty(name="Event Type",
items=[('LOOP', "Loop", "Loop Event"),
('UEVT', "UEVT", "UEVT Event"),
('EFFECT', "Effect", "Effect Event"),
('SOUND', "Sound", "Sound Event")],
default='LOOP')
index = bpy.props.IntProperty(name="Event Index")
time = bpy.props.FloatProperty(name="Event Time")
props = bpy.props.StringProperty(name="Event props")
loop_data = bpy.props.PointerProperty(name="Loop event data",
type=hecl_actor_event_loop)
uevt_data = bpy.props.PointerProperty(name="UEVT event data",
type=hecl_actor_event_uevt)
effect_data = bpy.props.PointerProperty(name="Effect event data",
type=hecl_actor_event_effect)
sound_data = bpy.props.PointerProperty(name="Sound event data",
type=hecl_actor_event_sound)
# Panel draw
def draw(layout, context):
actor_data = context.scene.hecl_sact_data
armature = None
if actor_data.active_subtype >= 0:
if actor_data.active_subtype in range(len(actor_data.subtypes)):
subtype = actor_data.subtypes[actor_data.active_subtype]
if subtype and subtype.linked_armature in bpy.data.objects:
armature = bpy.data.objects[subtype.linked_armature]
row = layout.row(align=True)
row.alignment = 'LEFT'
row.prop(actor_data, 'show_events', text="Events", icon='PREVIEW_RANGE', emboss=False)
if actor_data.show_events:
# Get action
action_data = None
subaction_data = None
if actor_data.active_action in range(len(actor_data.actions)):
action_data = actor_data.actions[actor_data.active_action]
if action_data.type == 'SINGLE':
subaction_data = action_data.subactions[0]
elif action_data.type == 'SEQUENCE' or action_data.type == 'RANDOM':
if action_data.active_subaction in range(len(action_data.subactions)):
subaction_data = action_data.subactions[action_data.active_subaction]
# Validate
if subaction_data is None:
layout.label("(Sub)action not selected in 'Actions'", icon='ERROR')
else:
if subaction_data.name == '':
layout.label("Action not set", icon='ERROR')
elif subaction_data.name not in bpy.data.actions:
layout.label("Action '"+subaction_data.name+"' not found", icon='ERROR')
else:
action = bpy.data.actions[subaction_data.name]
# Event list
row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_hecl_actor_subaction_events",
action, 'hecl_events', action, 'hecl_active_event')
col = row.column(align=True)
col.operator("scene.hecl_actor_subaction_event_add", icon="ZOOMIN", text="")
col.operator("scene.hecl_actor_subaction_event_remove", icon="ZOOMOUT", text="")
col.separator()
col.operator("scene.hecl_actor_subaction_event_move_up", icon="TRIA_UP", text="")
col.operator("scene.hecl_actor_subaction_event_move_down", icon="TRIA_DOWN", text="")
if len(action.hecl_events) and action.hecl_active_event >= 0:
event = action.hecl_events[action.hecl_active_event]
layout.prop(event, 'name', text="Name")
layout.prop(event, 'index', text="Index")
layout.prop(event, 'props', text="Props")
layout.label('Marker Time: ' + '{:g}'.format(event.time), icon='MARKER_HLT')
layout.label("Event Type:")
row = layout.row(align=True)
row.prop_enum(event, 'type', 'LOOP')
row.prop_enum(event, 'type', 'UEVT')
row.prop_enum(event, 'type', 'EFFECT')
row.prop_enum(event, 'type', 'SOUND')
if event.type == 'LOOP':
loop_data = event.loop_data
layout.prop(loop_data, 'bool')
elif event.type == 'UEVT':
uevt_data = event.uevt_data
layout.prop(uevt_data, 'type')
layout.prop(uevt_data, 'bone_name')
elif event.type == 'EFFECT':
effect_data = event.effect_data
layout.prop(effect_data, 'frame_count')
layout.prop(effect_data, 'uid')
if armature:
layout.prop_search(effect_data, 'bone_name', armature.data, 'bones')
else:
layout.prop(effect_data, 'bone_name')
layout.prop(effect_data, 'scale')
row = layout.row(align=True)
row.prop_enum(effect_data, 'transform_mode', 'STATIONARY')
row.prop_enum(effect_data, 'transform_mode', 'WORLD')
row.prop_enum(effect_data, 'transform_mode', 'LOCAL')
elif event.type == 'SOUND':
sound_data = event.sound_data
layout.prop(sound_data, 'sound_id')
layout.prop(sound_data, 'ref_amp')
layout.prop(sound_data, 'ref_dist')
# Clear event markers not in active event
def clear_event_markers(actor_data, context):
for marker in context.scene.timeline_markers:
if marker.name.startswith('hecl_'):
context.scene.timeline_markers.remove(marker)
# Event marker update
@bpy.app.handlers.persistent
def update_action_events(dummy):
context = bpy.context
if context.scene.hecl_type == 'ACTOR':
actor_data = context.scene.hecl_sact_data
if actor_data.active_action in range(len(actor_data.actions)):
action_data = actor_data.actions[actor_data.active_action]
if action_data.name in bpy.data.actions:
action_obj =\
bpy.data.actions[action_data.name]
for i in range(len(action_obj.hecl_events)):
event = action_obj.hecl_events[i]
marker_name = 'hecl_' + str(action_obj.hecl_index) + '_' + str(i) + '_' + event.name
if marker_name in context.scene.timeline_markers:
marker = context.scene.timeline_markers[marker_name]
event_time = marker.frame / action_obj.hecl_fps
if event_time != event.time:
event.time = event_time
else:
marker = context.scene.timeline_markers.new(marker_name)
marker.frame = event.time * action_obj.hecl_fps
marker.select = False
if i != action_obj.hecl_active_event and marker.select:
action_obj.hecl_active_event = i
# Event 'add' operator
class hecl_actor_subaction_event_add(bpy.types.Operator):
bl_idname = "scene.hecl_actor_subaction_event_add"
bl_label = "New HECL Actor Event"
bl_description = "Add New HECL Actor Event to active Sub-action"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
check = (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
len(actor_data.actions) and
actor_data.active_action >= 0 and
len(actor_data.actions[actor_data.active_action].subactions) and
actor_data.actions[actor_data.active_action].active_subaction >= 0)
if not check:
return False
actor_data = context.scene.hecl_sact_data
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
return subaction_data.name in bpy.data.actions
def execute(self, context):
actor_data = context.scene.hecl_sact_data
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
blend_action = bpy.data.actions[subaction_data.name]
event_name = 'SubactionEvent'
if event_name in blend_action.hecl_events:
event_name = 'SubactionEvent.001'
event_idx = 1
while event_name in blend_action.hecl_events:
event_idx += 1
event_name = 'SubactionEvent.{:0>3}'.format(event_idx)
event = blend_action.hecl_events.add()
event.name = event_name
action_obj =\
bpy.data.actions[subaction_data.name]
event.time = (context.scene.frame_current / (context.scene.render.frame_map_new / context.scene.render.frame_map_old)) / action_obj.hecl_fps
blend_action.hecl_active_event = len(blend_action.hecl_events)-1
if not bpy.app.background:
update_action_events(None)
active_event_update(self, context)
return {'FINISHED'}
# Event 'remove' operator
class hecl_actor_subaction_event_remove(bpy.types.Operator):
bl_idname = "scene.hecl_actor_subaction_event_remove"
bl_label = "Remove HECL Actor Event"
bl_description = "Remove HECL Actor Event from active Sub-action"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
check = (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
len(actor_data.actions) and
actor_data.active_action >= 0 and
len(actor_data.actions[actor_data.active_action].subactions) and
actor_data.actions[actor_data.active_action].active_subaction >= 0)
if not check:
return False
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
if subaction_data.name not in bpy.data.actions:
return False
blend_action = bpy.data.actions[subaction_data.name]
return blend_action.hecl_active_event in range(len(blend_action.hecl_events))
def execute(self, context):
actor_data = context.scene.hecl_sact_data
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
blend_action = bpy.data.actions[subaction_data.name]
event_name = blend_action.hecl_events[blend_action.hecl_active_event].name
blend_action.hecl_events.remove(blend_action.hecl_active_event)
marker_name = 'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event) + '_' + event_name
if marker_name in context.scene.timeline_markers:
context.scene.timeline_markers.remove(context.scene.timeline_markers[marker_name])
blend_action.hecl_active_event -= 1
if blend_action.hecl_active_event == -1:
blend_action.hecl_active_event = 0
clear_event_markers(actor_data, context)
return {'FINISHED'}
# Event 'move down' operator
class hecl_actor_subaction_event_move_down(bpy.types.Operator):
bl_idname = "scene.hecl_actor_subaction_event_move_down"
bl_label = "Move HECL Actor Event Down in Stack"
bl_description = "Move HECL Actor Event down in stack from active Sub-action"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
check = (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
len(actor_data.actions) and
actor_data.active_action >= 0 and
len(actor_data.actions[actor_data.active_action].subactions) and
actor_data.actions[actor_data.active_action].active_subaction >= 0)
if not check:
return False
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
if subaction_data.name not in bpy.data.actions:
return False
blend_action = bpy.data.actions[subaction_data.name]
return (blend_action.hecl_active_event in range(len(blend_action.hecl_events)) and
blend_action.hecl_active_event < len(blend_action.hecl_events) - 1)
def execute(self, context):
actor_data = context.scene.hecl_sact_data
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
blend_action = bpy.data.actions[subaction_data.name]
event_name_a = blend_action.hecl_events[blend_action.hecl_active_event].name
event_name_b = blend_action.hecl_events[blend_action.hecl_active_event + 1].name
blend_action.hecl_events.move(blend_action.hecl_active_event, blend_action.hecl_active_event + 1)
marker_name_a = 'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event) + '_' + event_name_a
marker_a = None
if marker_name_a in context.scene.timeline_markers:
marker_a = context.scene.timeline_markers[marker_name_a]
marker_name_b = 'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event + 1) + '_' + event_name_b
marker_b = None
if marker_name_b in context.scene.timeline_markers:
marker_b = context.scene.timeline_markers[marker_name_b]
if marker_a and marker_b:
marker_a.name =\
'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event + 1) + '_' + event_name_a
marker_b.name =\
'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event) + '_' + event_name_b
blend_action.hecl_active_event += 1
return {'FINISHED'}
# Event 'move up' operator
class hecl_actor_subaction_event_move_up(bpy.types.Operator):
bl_idname = "scene.hecl_actor_subaction_event_move_up"
bl_label = "Move HECL Actor Event Up in Stack"
bl_description = "Move HECL Actor Event up in stack from active Sub-action"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
check = (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
len(actor_data.actions) and
actor_data.active_action >= 0 and
len(actor_data.actions[actor_data.active_action].subactions) and
actor_data.actions[actor_data.active_action].active_subaction >= 0)
if not check:
return False
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
if subaction_data.name not in bpy.data.actions:
return False
blend_action = bpy.data.actions[subaction_data.name]
return (blend_action.hecl_active_event in range(len(blend_action.hecl_events)) and
blend_action.hecl_active_event > 0)
def execute(self, context):
actor_data = context.scene.hecl_sact_data
action_data = actor_data.actions[actor_data.active_action]
subaction_data = action_data.subactions[action_data.active_subaction]
blend_action = bpy.data.actions[subaction_data.name]
event_name_a = blend_action.hecl_events[blend_action.hecl_active_event].name
event_name_b = blend_action.hecl_events[blend_action.hecl_active_event - 1].name
blend_action.hecl_events.move(blend_action.hecl_active_event, blend_action.hecl_active_event - 1)
marker_name_a = 'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event) + '_' + event_name_a
marker_a = None
if marker_name_a in context.scene.timeline_markers:
marker_a = context.scene.timeline_markers[marker_name_a]
marker_name_b = 'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event - 1) + '_' + event_name_b
marker_b = None
if marker_name_b in context.scene.timeline_markers:
marker_b = context.scene.timeline_markers[marker_name_b]
if marker_a and marker_b:
marker_a.name =\
'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event - 1) + '_' + event_name_a
marker_b.name =\
'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event) + '_' + event_name_b
blend_action.hecl_active_event -= 1
return {'FINISHED'}
def active_event_update(self, context):
actor_data = context.scene.hecl_sact_data
if actor_data.active_action in range(len(actor_data.actions)):
action_data = actor_data.actions[actor_data.active_action]
for marker in context.scene.timeline_markers:
if marker.name.startswith('hecl_'):
blend_action = bpy.data.actions[action_data.name]
event_name = blend_action.hecl_events[blend_action.hecl_active_event].name
if marker.name == 'hecl_' + str(blend_action.hecl_index) + '_' + str(blend_action.hecl_active_event) + '_' + event_name:
marker.select = True
else:
marker.select = False
# Registration
def register():
bpy.utils.register_class(hecl_actor_event_loop)
bpy.utils.register_class(hecl_actor_event_uevt)
bpy.utils.register_class(hecl_actor_event_effect)
bpy.utils.register_class(hecl_actor_event_sound)
bpy.utils.register_class(hecl_actor_event)
bpy.utils.register_class(hecl_actor_subaction_event_add)
bpy.utils.register_class(hecl_actor_subaction_event_remove)
bpy.utils.register_class(hecl_actor_subaction_event_move_down)
bpy.utils.register_class(hecl_actor_subaction_event_move_up)
bpy.types.Action.hecl_events = bpy.props.CollectionProperty(name="HECL action event",
type=hecl_actor_event)
bpy.types.Action.hecl_active_event = bpy.props.IntProperty(name="HECL active action event",
default=0,
update=active_event_update)
if not bpy.app.background and update_action_events not in bpy.app.handlers.scene_update_post:
bpy.app.handlers.scene_update_post.append(update_action_events)
def unregister():
if update_action_events in bpy.app.handlers.scene_update_post:
bpy.app.handlers.scene_update_post.remove(update_action_events)
bpy.utils.unregister_class(hecl_actor_event)
bpy.utils.unregister_class(hecl_actor_event_loop)
bpy.utils.unregister_class(hecl_actor_event_uevt)
bpy.utils.unregister_class(hecl_actor_event_effect)
bpy.utils.unregister_class(hecl_actor_event_sound)
bpy.utils.unregister_class(hecl_actor_subaction_event_add)
bpy.utils.unregister_class(hecl_actor_subaction_event_remove)
bpy.utils.unregister_class(hecl_actor_subaction_event_move_down)
bpy.utils.unregister_class(hecl_actor_subaction_event_move_up)

View File

@ -236,15 +236,15 @@ class SACTSubtypeOverlay_add(bpy.types.Operator):
def execute(self, context):
actor_data = context.scene.hecl_sact_data
subtype = actor_data.subtypes[actor_data.active_subtype]
overlay_name = 'ActorMesh'
overlay_name = 'ActorOverlay'
if overlay_name in subtype.overlays:
overlay_name = 'ActorMesh.001'
overlay_name = 'ActorOverlay.001'
overlay_idx = 1
while overlay_name in subtype.overlays:
overlay_idx += 1
overlay_name = 'ActorMesh.{:0>3}'.format(overlay_idx)
overlay_name = 'ActorOverlay.{:0>3}'.format(overlay_idx)
overlay = subtype.overlays.add()
mesh.name = overlay_name
overlay.name = overlay_name
subtype.active_overlay = len(subtype.overlays)-1
return {'FINISHED'}

View File

@ -2,11 +2,12 @@ from . import SACTSubtype, SACTAction, ANIM
from .. import hmdl
import bpy
import bpy.path
import re
import os.path
import posixpath
import struct
from mathutils import Vector
from mathutils import Vector, Quaternion, Euler
# Actor data class
class SACTData(bpy.types.PropertyGroup):
@ -25,113 +26,145 @@ class SACTData(bpy.types.PropertyGroup):
show_actions =\
bpy.props.BoolProperty()
#show_events =\
#bpy.props.BoolProperty()
# Regex RNA path matchers
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
location_matcher = re.compile(r'pose.bones\["(\S+)"\].location')
# A Routine to resolve HECL DAG-relative paths while ensuring database path-constraints
def resolve_local_path(blend_path, rel_path):
if not rel_path.startswith('//'):
raise RuntimeError("Files must have relative paths")
blend_dir = os.path.split(blend_path)[0]
image_comps = re.split('/|\\\\', rel_path[2:])
start_idx = 0
for comp in image_comps:
if comp == '..':
start_idx += 1
if not blend_dir:
raise RuntimeError("Relative file path has exceeded DAG root")
blend_dir = os.path.split(blend_dir)[0]
def write_action_channels(writebuf, action):
# Set of frame indices
frame_set = set()
# Set of unique bone names
bone_set = []
# Scan through all fcurves to build animated bone set
for fcurve in action.fcurves:
data_path = fcurve.data_path
scale_match = scale_matcher.match(data_path)
rotation_match = rotation_matcher.match(data_path)
location_match = location_matcher.match(data_path)
if scale_match:
if scale_match.group(1) not in bone_set:
bone_set.append(scale_match.group(1))
elif rotation_match:
if rotation_match.group(1) not in bone_set:
bone_set.append(rotation_match.group(1))
elif location_match:
if location_match.group(1) not in bone_set:
bone_set.append(location_match.group(1))
else:
break
retval = blend_dir
for i in range(len(image_comps)-start_idx):
if retval:
retval += '/'
retval += image_comps[start_idx+i]
return posixpath.relpath(retval)
continue
# Count unified keyframes for interleaving channel data
for key in fcurve.keyframe_points:
frame_set.add(int(key.co[0]))
# Build bone table
bone_list = []
for bone in bone_set:
fc_dict = dict()
rotation_mode = None
property_bits = 0
for fcurve in action.fcurves:
if fcurve.data_path == 'pose.bones["'+bone+'"].scale':
if 'scale' not in fc_dict:
fc_dict['scale'] = [None, None, None]
property_bits |= 4
fc_dict['scale'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_euler':
if 'rotation_euler' not in fc_dict:
fc_dict['rotation_euler'] = [None, None, None]
rotation_mode = 'rotation_euler'
property_bits |= 1
fc_dict['rotation_euler'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_quaternion':
if 'rotation_quaternion' not in fc_dict:
fc_dict['rotation_quaternion'] = [None, None, None, None]
rotation_mode = 'rotation_quaternion'
property_bits |= 1
fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_axis_angle':
if 'rotation_axis_angle' not in fc_dict:
fc_dict['rotation_axis_angle'] = [None, None, None, None]
rotation_mode = 'rotation_axis_angle'
property_bits |= 1
fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].location':
if 'location' not in fc_dict:
fc_dict['location'] = [None, None, None]
property_bits |= 2
fc_dict['location'][fcurve.array_index] = fcurve
bone_list.append((bone, rotation_mode, fc_dict, property_bits))
# Write out frame indices
sorted_frames = sorted(frame_set)
writebuf(struct.pack('I', len(sorted_frames)))
for frame in sorted_frames:
writebuf(struct.pack('i', frame))
# Interleave / interpolate keyframe data
writebuf(struct.pack('I', len(bone_list)))
for bone in bone_list:
bone_name = bone[0]
rotation_mode = bone[1]
fc_dict = bone[2]
property_bits = bone[3]
writebuf(struct.pack('I', len(bone_name)))
writebuf(bone_name.encode())
writebuf(struct.pack('I', property_bits))
writebuf(struct.pack('I', len(sorted_frames)))
for frame in sorted_frames:
# Rotation curves
if rotation_mode == 'rotation_quaternion':
writevec = [0.0]*4
for comp in range(4):
if fc_dict['rotation_quaternion'][comp]:
writevec[comp] = fc_dict['rotation_quaternion'][comp].evaluate(frame)
writebuf(struct.pack('ffff', writevec[0], writevec[1], writevec[2], writevec[3]))
elif rotation_mode == 'rotation_euler':
euler = [0.0, 0.0, 0.0]
for comp in range(3):
if fc_dict['rotation_euler'][comp]:
euler[comp] = fc_dict['rotation_euler'][comp].evaluate(frame)
euler_o = Euler(euler, 'XYZ')
quat = euler_o.to_quaternion()
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
elif rotation_mode == 'rotation_axis_angle':
axis_angle = [0.0, 0.0, 0.0, 0.0]
for comp in range(4):
if fc_dict['rotation_axis_angle'][comp]:
axis_angle[comp] = fc_dict['rotation_axis_angle'][comp].evaluate(frame)
quat = Quaternion(axis_angle[1:4], axis_angle[0])
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
# Location curves
if 'location' in fc_dict:
writevec = [0.0]*3
for comp in range(3):
if fc_dict['location'][comp]:
writevec[comp] = fc_dict['location'][comp].evaluate(frame)
writebuf(struct.pack('fff', writevec[0], writevec[1], writevec[2]))
# Scale curves
if 'scale' in fc_dict:
writevec = [1.0]*3
for comp in range(3):
if fc_dict['scale'][comp]:
writevec[comp] = fc_dict['scale'][comp].evaluate(frame)
writebuf(struct.pack('fff', writevec[0], writevec[1], writevec[2]))
# RARM Generator
def package_rarm(arm_obj, res_db, heclpak, arg_path, arg_package):
rarm_db_id, rarm_hash = res_db.register_resource(arg_path, arm_obj.name, arg_package)
if not rarm_hash:
skeleton_data = hecl_rmdl.generate_skeleton_info(arm_obj)
rarm_hash = heclpak.add_object(skeleton_data, b'RARM')
res_db.update_resource_stats(rarm_db_id, rarm_hash)
return rarm_db_id, rarm_hash
# RANI Generator
def package_rani(action_obj, res_db, heclpak, arg_path, arg_package):
rani_db_id, rani_hash = res_db.register_resource(arg_path, action_obj.name, arg_package)
if not rani_hash:
res_db.clear_dependencies(rani_db_id)
rani_hash = heclpak.add_object(hecl_rmdl.rmdl_anim.generate_animation_info(action_obj, res_db, rani_db_id, arg_package), b'RANI')
res_db.update_resource_stats(rani_db_id, rani_hash)
return rani_db_id, rani_hash
# Actor Ticket Generator
def package_actor(scene, res_db, heclpak, arg_path, arg_package, arg_res_name):
actor_data = scene.hecl_sact_data
act_db_id, act_hash = res_db.register_resource(arg_path, None, arg_package)
res_db.clear_dependencies(act_db_id)
with open(os.path.splitext(bpy.data.filepath)[0] + '.heclticket', 'wb') as ticket:
# Subtypes
ticket.write(struct.pack('I', len(actor_data.subtypes)))
for subtype_idx in range(len(actor_data.subtypes)):
subtype = actor_data.subtypes[subtype_idx]
scene.hecl_sact_data.active_subtype = subtype_idx
# Subtype name
ticket.write(subtype.name.encode() + b'\0')
# Mesh
if subtype.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[subtype.linked_mesh]
if mesh_obj.library:
path = resolve_local_path(arg_path.split(';')[-1], mesh_obj.library.filepath)
mesh_db_id, mesh_hash = res_db.search_for_resource(path, arg_package)
if not mesh_hash:
raise RuntimeError("Error - unable to load mesh library '{0}'".format(path))
res_db.register_dependency(act_db_id, mesh_db_id)
ticket.write(mesh_hash)
else:
mesh_db_id, mesh_hash, _final_data = hecl_rmdl.to_rmdl(mesh_obj, mesh_obj.name, res_db, heclpak, arg_path, arg_package)
res_db.register_dependency(act_db_id, mesh_db_id)
ticket.write(mesh_hash)
else:
raise RuntimeError("Error - unable to load mesh '{0}'".format(mesh))
# Armature
if subtype.linked_armature in bpy.data.objects:
arm_obj = bpy.data.objects[subtype.linked_armature]
rarm_db_id, rarm_hash = package_rarm(arm_obj, res_db, heclpak, arg_path, arg_package)
res_db.register_dependency(act_db_id, rarm_db_id)
ticket.write(rarm_hash)
else:
raise RuntimeError("Error - unable to load armature '{0}'".format(subtype.linked_armature))
# Action AABBs
print('\nComputing Action AABBs for', subtype.name)
scene.hecl_auto_remap = False
ticket.write(struct.pack('I', len(actor_data.actions)))
for action_idx in range(len(actor_data.actions)):
action = actor_data.actions[action_idx]
print(action.name)
scene.hecl_sact_data.active_action = action_idx
bpy.ops.scene.SACTAction_load()
# Action name
ticket.write(action.name.encode() + b'\0')
def write_action_aabb(writebuf, arm_obj, mesh_obj):
scene = bpy.context.scene
# Frame 1
scene.frame_set(1)
@ -166,76 +199,116 @@ def package_actor(scene, res_db, heclpak, arg_path, arg_package, arg_res_name):
if test_aabb_max[comp] > root_aabb_max[comp]:
root_aabb_max[comp] = test_aabb_max[comp]
ticket.write(struct.pack('ffffff',
writebuf(struct.pack('ffffff',
root_aabb_min[0], root_aabb_min[1], root_aabb_min[2],
root_aabb_max[0], root_aabb_max[1], root_aabb_max[2]))
# Actions
anim_hashes = dict()
ticket.write(struct.pack('I', len(actor_data.actions)))
for action in actor_data.actions:
# Action name
ticket.write(action.name.encode() + b'\0')
if action.type == 'SINGLE':
ticket.write(struct.pack('I', 0))
action_name = action.subactions[0].name
if action_name not in bpy.data.actions:
raise RuntimeError("Error - unable to load action '{0}'".format(action_name))
if action_name in anim_hashes:
rani_hash = anim_hashes[action_name]
else:
action_obj = bpy.data.actions[action_name]
rani_db_id, rani_hash = package_rani(action_obj, res_db, heclpak, arg_path, arg_package)
res_db.register_dependency(act_db_id, rani_db_id)
anim_hashes[action_name] = rani_hash
ticket.write(rani_hash)
elif action.type == 'SEQUENCE':
ticket.write(struct.pack('II', 1, len(action.subactions)))
for subaction in action.subactions:
action_name = subaction.name
if action_name not in bpy.data.actions:
raise RuntimeError("Error - unable to load action '{0}'".format(action_name))
if action_name in anim_hashes:
rani_hash = anim_hashes[action_name]
else:
action_obj = bpy.data.actions[action_name]
rani_db_id, rani_hash = package_rani(action_obj,
res_db, heclpak, arg_path, arg_package)
res_db.register_dependency(act_db_id, rani_db_id)
anim_hashes[action_name] = rani_hash
ticket.write(rani_hash)
elif action.type == 'RANDOM':
ticket.write(struct.pack('III', 2, len(action.subactions), action.random_val))
for subaction in action.subactions:
action_name = subaction.name
if action_name not in bpy.data.actions:
raise RuntimeError("Error - unable to load action '{0}'".format(action_name))
if action_name in anim_hashes:
rani_hash = anim_hashes[action_name]
else:
action_obj = bpy.data.actions[action_name]
rani_db_id, rani_hash = package_rani(action_obj,
res_db, heclpak, arg_path, arg_package)
res_db.register_dependency(act_db_id, rani_db_id)
anim_hashes[action_name] = rani_hash
ticket.write(rani_hash)
# Cook
def cook(writebuffunc, platform, endianchar):
print('COOKING SACT')
def cook(writebuf):
sact_data = bpy.context.scene.hecl_sact_data
# Output armatures
writebuf(struct.pack('I', len(bpy.data.armatures)))
for arm in bpy.data.armatures:
writebuf(struct.pack('I', len(arm.name)))
writebuf(arm.name.encode())
writebuf(struct.pack('I', len(arm.bones)))
for bone in arm.bones:
writebuf(struct.pack('I', len(bone.name)))
writebuf(bone.name.encode())
writebuf(struct.pack('fff', bone.head_local[0], bone.head_local[1], bone.head_local[2]))
if bone.parent:
writebuf(struct.pack('i', arm.bones.find(bone.parent.name)))
else:
writebuf(struct.pack('i', -1))
writebuf(struct.pack('I', len(bone.children)))
for child in bone.children:
writebuf(struct.pack('i', arm.bones.find(child.name)))
# Output subtypes
writebuf(struct.pack('I', len(sact_data.subtypes)))
for subtype in sact_data.subtypes:
writebuf(struct.pack('I', len(subtype.name)))
writebuf(subtype.name.encode())
mesh = None
if subtype.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[subtype.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 subtype.linked_armature in bpy.data.objects:
arm = bpy.data.objects[subtype.linked_armature]
arm_idx = -1
if arm:
arm_idx = bpy.data.armatures.find(arm.name)
writebuf(struct.pack('i', arm_idx))
writebuf(struct.pack('I', len(subtype.overlays)))
for overlay in subtype.overlays:
writebuf(struct.pack('I', len(overlay.name)))
writebuf(overlay.name.encode())
mesh = None
if overlay.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[overlay.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))
# Output actions
writebuf(struct.pack('I', len(sact_data.actions)))
for action_idx in range(len(sact_data.actions)):
sact_data.active_action = action_idx
action = sact_data.actions[action_idx]
writebuf(struct.pack('I', len(action.name)))
writebuf(action.name.encode())
bact = None
if action.name in bpy.data.actions:
bact = bpy.data.actions[action.name]
if not bact:
raise RuntimeError('action %s not found' % action.name)
writebuf(struct.pack('f', 1.0 / bact.hecl_fps))
writebuf(struct.pack('b', int(bact.hecl_additive)))
write_action_channels(writebuf, bact)
writebuf(struct.pack('I', len(sact_data.subtypes)))
for subtype_idx in range(len(sact_data.subtypes)):
subtype = sact_data.subtypes[subtype_idx]
sact_data.active_subtype = subtype_idx
bpy.ops.scene.sactaction_load()
if subtype.linked_armature not in bpy.data.objects:
raise RuntimeError('armature %s not found' % subtype.linked_armature)
arm = bpy.data.objects[subtype.linked_armature]
if subtype.linked_mesh not in bpy.data.objects:
raise RuntimeError('mesh %s not found' % subtype.linked_mesh)
mesh = bpy.data.objects[subtype.linked_mesh]
write_action_aabb(writebuf, arm, mesh)
# Panel draw
def draw(layout, context):
SACTSubtype.draw(layout.box(), context)
SACTAction.draw(layout.box(), context)
#SACTEvent.draw(layout.box(), context)
# Time-remap option update
@ -247,7 +320,6 @@ def time_remap_update(self, context):
# Registration
def register():
SACTSubtype.register()
#SACTEvent.register()
SACTAction.register()
bpy.utils.register_class(SACTData)
bpy.types.Scene.hecl_sact_data = bpy.props.PointerProperty(type=SACTData)
@ -261,4 +333,3 @@ def unregister():
bpy.utils.unregister_class(SACTData)
SACTSubtype.unregister()
SACTAction.unregister()
#SACTEvent.unregister()

View File

@ -184,6 +184,10 @@ def dataout_loop():
bpy.data.objects.remove(join_obj)
bpy.data.meshes.remove(join_mesh)
elif cmdargs[0] == 'ACTORCOMPILE':
writepipeline(b'OK')
hecl.sact.cook(writepipebuf)
# Command loop
while True: