mirror of https://github.com/AxioDL/metaforce.git
initial actor cooking support
This commit is contained in:
parent
bcee8aa897
commit
fc64c4d4a4
|
@ -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");
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
'''
|
||||
HMDL Export Blender Addon
|
||||
By Jack Andersen <jackoalan@gmail.com>
|
||||
'''
|
||||
|
||||
import bpy, bmesh, operator, struct
|
||||
from mathutils import Vector
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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'}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue