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; Mesh newMesh = *this;
newMesh.pos.clear(); newMesh.pos.clear();
newMesh.norm.clear(); newMesh.norm.clear();
newMesh.contiguousSkinVertCounts.clear();
newMesh.contiguousSkinVertCounts.reserve(skins.size()); newMesh.contiguousSkinVertCounts.reserve(skins.size());
for (size_t i=0 ; i<skins.size() ; ++i) for (size_t i=0 ; i<skins.size() ; ++i)
{ {
@ -754,6 +755,174 @@ uint32_t BlenderConnection::DataStream::Mesh::SkinBanks::addSurface
return uint32_t(-1); 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() void BlenderConnection::quitBlender()
{ {
_writeLine("QUIT"); _writeLine("QUIT");

View File

@ -66,6 +66,13 @@ public:
bool saveBlend(); bool saveBlend();
void deleteBlend(); void deleteBlend();
enum ANIMCurveType
{
CurveRotate,
CurveTranslate,
CurveScale
};
class PyOutStream : public std::ostream class PyOutStream : public std::ostream
{ {
friend class BlenderConnection; friend class BlenderConnection;
@ -165,12 +172,7 @@ public:
unsigned m_totalCount = 0; unsigned m_totalCount = 0;
bool m_inCurve = false; bool m_inCurve = false;
public: public:
enum CurveType using CurveType = ANIMCurveType;
{
CurveRotate,
CurveTranslate,
CurveScale
};
ANIMOutStream(BlenderConnection* parent) ANIMOutStream(BlenderConnection* parent)
: m_parent(parent) : m_parent(parent)
{ {
@ -283,28 +285,43 @@ public:
return retval; return retval;
} }
/* Intermediate mesh representation prepared by blender from a single mesh object */ /* Vector types with integrated stream reading constructor */
struct Vector2f
{
atVec2f val;
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() = 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() = 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 struct Mesh
{ {
struct Vector2f
{
atVec2f val;
Vector2f(BlenderConnection& conn) {conn._readBuf(&val, 8);}
operator const atVec2f&() const {return val;}
};
struct Vector3f
{
atVec3f val;
Vector3f(BlenderConnection& conn) {conn._readBuf(&val, 12);}
operator const atVec3f&() const {return val;}
};
struct Index
{
uint32_t val;
Index(BlenderConnection& conn) {conn._readBuf(&val, 4);}
operator const uint32_t&() const {return val;}
};
enum OutputMode enum OutputMode
{ {
OutputTriangles, 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 compileMesh(Mesh::OutputMode outMode, int skinSlotCount=10,
Mesh::SurfProgFunc surfProg=[](int){}) 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]; char req[128];
snprintf(req, 128, "MESHCOMPILE %s %d", snprintf(req, 128, "MESHCOMPILE %s %d",
MeshOutputModeString(outMode), skinSlotCount); MeshOutputModeString(outMode), skinSlotCount);
@ -418,10 +439,14 @@ public:
return Mesh(*m_parent, outMode, skinSlotCount, surfProg); 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 compileMesh(const std::string& name, Mesh::OutputMode outMode, int skinSlotCount=10,
Mesh::SurfProgFunc surfProg=[](int){}) 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]; char req[128];
snprintf(req, 128, "MESHCOMPILENAME %s %s %d", name.c_str(), snprintf(req, 128, "MESHCOMPILENAME %s %s %d", name.c_str(),
MeshOutputModeString(outMode), skinSlotCount); MeshOutputModeString(outMode), skinSlotCount);
@ -435,10 +460,14 @@ public:
return Mesh(*m_parent, outMode, skinSlotCount, surfProg); 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 compileAllMeshes(Mesh::OutputMode outMode, int skinSlotCount=10, float maxOctantLength=5.0,
Mesh::SurfProgFunc surfProg=[](int){}) 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]; char req[128];
snprintf(req, 128, "MESHCOMPILEALL %s %d %f", snprintf(req, 128, "MESHCOMPILEALL %s %d %f",
MeshOutputModeString(outMode), MeshOutputModeString(outMode),
@ -452,6 +481,87 @@ public:
return Mesh(*m_parent, outMode, skinSlotCount, surfProg); 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() DataStream beginData()
{ {

View File

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

View File

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

View File

@ -1,8 +1,3 @@
'''
HMDL Export Blender Addon
By Jack Andersen <jackoalan@gmail.com>
'''
import bpy, bmesh, operator, struct import bpy, bmesh, operator, struct
from mathutils import Vector 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 import bpy, bpy.path, os.path
def get_texmap_idx(tex_list, name): 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 import struct, bpy, bmesh
from mathutils import Vector from mathutils import Vector
from . import HMDLShader, HMDLSkin, HMDLMesh from . import HMDLShader, 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
def write_out_material(writebuf, mat, mesh_obj): def write_out_material(writebuf, mat, mesh_obj):
hecl_str, texs = HMDLShader.shader(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): def execute(self, context):
actor_data = context.scene.hecl_sact_data actor_data = context.scene.hecl_sact_data
subtype = actor_data.subtypes[actor_data.active_subtype] subtype = actor_data.subtypes[actor_data.active_subtype]
overlay_name = 'ActorMesh' overlay_name = 'ActorOverlay'
if overlay_name in subtype.overlays: if overlay_name in subtype.overlays:
overlay_name = 'ActorMesh.001' overlay_name = 'ActorOverlay.001'
overlay_idx = 1 overlay_idx = 1
while overlay_name in subtype.overlays: while overlay_name in subtype.overlays:
overlay_idx += 1 overlay_idx += 1
overlay_name = 'ActorMesh.{:0>3}'.format(overlay_idx) overlay_name = 'ActorOverlay.{:0>3}'.format(overlay_idx)
overlay = subtype.overlays.add() overlay = subtype.overlays.add()
mesh.name = overlay_name overlay.name = overlay_name
subtype.active_overlay = len(subtype.overlays)-1 subtype.active_overlay = len(subtype.overlays)-1
return {'FINISHED'} return {'FINISHED'}

View File

@ -2,11 +2,12 @@ from . import SACTSubtype, SACTAction, ANIM
from .. import hmdl from .. import hmdl
import bpy import bpy
import bpy.path
import re import re
import os.path import os.path
import posixpath import posixpath
import struct import struct
from mathutils import Vector from mathutils import Vector, Quaternion, Euler
# Actor data class # Actor data class
class SACTData(bpy.types.PropertyGroup): class SACTData(bpy.types.PropertyGroup):
@ -25,217 +26,289 @@ class SACTData(bpy.types.PropertyGroup):
show_actions =\ show_actions =\
bpy.props.BoolProperty() bpy.props.BoolProperty()
#show_events =\ # Regex RNA path matchers
#bpy.props.BoolProperty() 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 write_action_channels(writebuf, action):
def resolve_local_path(blend_path, rel_path): # Set of frame indices
if not rel_path.startswith('//'): frame_set = set()
raise RuntimeError("Files must have relative paths")
blend_dir = os.path.split(blend_path)[0] # Set of unique bone names
image_comps = re.split('/|\\\\', rel_path[2:]) bone_set = []
start_idx = 0
for comp in image_comps: # Scan through all fcurves to build animated bone set
if comp == '..': for fcurve in action.fcurves:
start_idx += 1 data_path = fcurve.data_path
if not blend_dir: scale_match = scale_matcher.match(data_path)
raise RuntimeError("Relative file path has exceeded DAG root") rotation_match = rotation_matcher.match(data_path)
blend_dir = os.path.split(blend_dir)[0] 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: else:
break continue
retval = blend_dir
for i in range(len(image_comps)-start_idx): # Count unified keyframes for interleaving channel data
if retval: for key in fcurve.keyframe_points:
retval += '/' frame_set.add(int(key.co[0]))
retval += image_comps[start_idx+i]
return posixpath.relpath(retval) # 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 write_action_aabb(writebuf, arm_obj, mesh_obj):
def package_rarm(arm_obj, res_db, heclpak, arg_path, arg_package): scene = bpy.context.scene
rarm_db_id, rarm_hash = res_db.register_resource(arg_path, arm_obj.name, arg_package) # Frame 1
if not rarm_hash: scene.frame_set(1)
skeleton_data = hecl_rmdl.generate_skeleton_info(arm_obj) # Transform against root
rarm_hash = heclpak.add_object(skeleton_data, b'RARM') root_bone = arm_obj.pose.bones['root']
res_db.update_resource_stats(rarm_db_id, rarm_hash) root_bone.location = (0.0,0.0,0.0)
if root_bone.rotation_mode == 'QUATERNION':
root_bone.rotation_quaternion = (1.0,0.0,0.0,0.0)
else:
root_bone.rotation_euler = (0.0,0.0,0.0)
root_aabb_min = Vector(mesh_obj.bound_box[0])
root_aabb_max = Vector(mesh_obj.bound_box[6])
return rarm_db_id, rarm_hash # Accumulate AABB for each frame
for frame_idx in range(2, scene.frame_end + 1):
scene.frame_set(frame_idx)
# RANI Generator root_bone.location = (0.0,0.0,0.0)
def package_rani(action_obj, res_db, heclpak, arg_path, arg_package): scene.update()
if root_bone.rotation_mode == 'QUATERNION':
root_bone.rotation_quaternion = (1.0,0.0,0.0,0.0)
else:
root_bone.rotation_euler = (0.0,0.0,0.0)
test_aabb_min = Vector(mesh_obj.bound_box[0])
test_aabb_max = Vector(mesh_obj.bound_box[6])
rani_db_id, rani_hash = res_db.register_resource(arg_path, action_obj.name, arg_package) for comp in range(3):
if not rani_hash: if test_aabb_min[comp] < root_aabb_min[comp]:
root_aabb_min[comp] = test_aabb_min[comp]
res_db.clear_dependencies(rani_db_id) for comp in range(3):
rani_hash = heclpak.add_object(hecl_rmdl.rmdl_anim.generate_animation_info(action_obj, res_db, rani_db_id, arg_package), b'RANI') if test_aabb_max[comp] > root_aabb_max[comp]:
res_db.update_resource_stats(rani_db_id, rani_hash) root_aabb_max[comp] = test_aabb_max[comp]
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')
# Frame 1
scene.frame_set(1)
# Transform against root
root_bone = arm_obj.pose.bones['root']
root_bone.location = (0.0,0.0,0.0)
if root_bone.rotation_mode == 'QUATERNION':
root_bone.rotation_quaternion = (1.0,0.0,0.0,0.0)
else:
root_bone.rotation_euler = (0.0,0.0,0.0)
root_aabb_min = Vector(mesh_obj.bound_box[0])
root_aabb_max = Vector(mesh_obj.bound_box[6])
# Accumulate AABB for each frame
for frame_idx in range(2, scene.frame_end + 1):
scene.frame_set(frame_idx)
root_bone.location = (0.0,0.0,0.0)
scene.update()
if root_bone.rotation_mode == 'QUATERNION':
root_bone.rotation_quaternion = (1.0,0.0,0.0,0.0)
else:
root_bone.rotation_euler = (0.0,0.0,0.0)
test_aabb_min = Vector(mesh_obj.bound_box[0])
test_aabb_max = Vector(mesh_obj.bound_box[6])
for comp in range(3):
if test_aabb_min[comp] < root_aabb_min[comp]:
root_aabb_min[comp] = test_aabb_min[comp]
for comp in range(3):
if test_aabb_max[comp] > root_aabb_max[comp]:
root_aabb_max[comp] = test_aabb_max[comp]
ticket.write(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)
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]))
# Cook # Cook
def cook(writebuffunc, platform, endianchar): def cook(writebuf):
print('COOKING SACT') 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 # Panel draw
def draw(layout, context): def draw(layout, context):
SACTSubtype.draw(layout.box(), context) SACTSubtype.draw(layout.box(), context)
SACTAction.draw(layout.box(), context) SACTAction.draw(layout.box(), context)
#SACTEvent.draw(layout.box(), context)
# Time-remap option update # Time-remap option update
@ -247,7 +320,6 @@ def time_remap_update(self, context):
# Registration # Registration
def register(): def register():
SACTSubtype.register() SACTSubtype.register()
#SACTEvent.register()
SACTAction.register() SACTAction.register()
bpy.utils.register_class(SACTData) bpy.utils.register_class(SACTData)
bpy.types.Scene.hecl_sact_data = bpy.props.PointerProperty(type=SACTData) bpy.types.Scene.hecl_sact_data = bpy.props.PointerProperty(type=SACTData)
@ -261,4 +333,3 @@ def unregister():
bpy.utils.unregister_class(SACTData) bpy.utils.unregister_class(SACTData)
SACTSubtype.unregister() SACTSubtype.unregister()
SACTAction.unregister() SACTAction.unregister()
#SACTEvent.unregister()

View File

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