mirror of https://github.com/AxioDL/metaforce.git
initial actor panel integration
This commit is contained in:
parent
461893d7a1
commit
1d4786344a
|
@ -1,12 +1,9 @@
|
||||||
#if _WIN32
|
|
||||||
#else
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <string>
|
#include <string>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "CBlenderConnection.hpp"
|
#include "CBlenderConnection.hpp"
|
||||||
|
|
||||||
|
@ -20,7 +17,7 @@
|
||||||
|
|
||||||
#define TEMP_SHELLSCRIPT "/home/jacko/hecl/blender/blendershell.py"
|
#define TEMP_SHELLSCRIPT "/home/jacko/hecl/blender/blendershell.py"
|
||||||
|
|
||||||
size_t CBlenderConnection::readLine(char* buf, size_t bufSz)
|
size_t CBlenderConnection::_readLine(char* buf, size_t bufSz)
|
||||||
{
|
{
|
||||||
size_t readBytes = 0;
|
size_t readBytes = 0;
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -51,7 +48,7 @@ err:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CBlenderConnection::writeLine(const char* buf)
|
size_t CBlenderConnection::_writeLine(const char* buf)
|
||||||
{
|
{
|
||||||
ssize_t ret, nlerr;
|
ssize_t ret, nlerr;
|
||||||
ret = write(m_writepipe[1], buf, strlen(buf));
|
ret = write(m_writepipe[1], buf, strlen(buf));
|
||||||
|
@ -65,7 +62,7 @@ err:
|
||||||
throw std::error_code(errno, std::system_category());
|
throw std::error_code(errno, std::system_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CBlenderConnection::readBuf(char* buf, size_t len)
|
size_t CBlenderConnection::_readBuf(char* buf, size_t len)
|
||||||
{
|
{
|
||||||
ssize_t ret = read(m_readpipe[0], buf, len);
|
ssize_t ret = read(m_readpipe[0], buf, len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -73,7 +70,7 @@ size_t CBlenderConnection::readBuf(char* buf, size_t len)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CBlenderConnection::writeBuf(const char* buf, size_t len)
|
size_t CBlenderConnection::_writeBuf(const char* buf, size_t len)
|
||||||
{
|
{
|
||||||
ssize_t ret = write(m_writepipe[1], buf, len);
|
ssize_t ret = write(m_writepipe[1], buf, len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -81,7 +78,7 @@ size_t CBlenderConnection::writeBuf(const char* buf, size_t len)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBlenderConnection::closePipe()
|
void CBlenderConnection::_closePipe()
|
||||||
{
|
{
|
||||||
close(m_readpipe[0]);
|
close(m_readpipe[0]);
|
||||||
close(m_writepipe[1]);
|
close(m_writepipe[1]);
|
||||||
|
@ -115,10 +112,11 @@ CBlenderConnection::CBlenderConnection(bool silenceBlender)
|
||||||
char writefds[32];
|
char writefds[32];
|
||||||
snprintf(writefds, 32, "%d", m_readpipe[1]);
|
snprintf(writefds, 32, "%d", m_readpipe[1]);
|
||||||
|
|
||||||
/* User-specified blender first */
|
/* Try user-specified blender first */
|
||||||
if (blenderBin)
|
if (blenderBin)
|
||||||
{
|
{
|
||||||
execlp(blenderBin, blenderBin, "--background", "-P", TEMP_SHELLSCRIPT,
|
execlp(blenderBin, blenderBin,
|
||||||
|
"--background", "-P", TEMP_SHELLSCRIPT,
|
||||||
"--", readfds, writefds, NULL);
|
"--", readfds, writefds, NULL);
|
||||||
if (errno != ENOENT)
|
if (errno != ENOENT)
|
||||||
{
|
{
|
||||||
|
@ -128,8 +126,9 @@ CBlenderConnection::CBlenderConnection(bool silenceBlender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default blender next */
|
/* Otherwise default blender */
|
||||||
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN, "--background", "-P", TEMP_SHELLSCRIPT,
|
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
|
||||||
|
"--background", "-P", TEMP_SHELLSCRIPT,
|
||||||
"--", readfds, writefds, NULL);
|
"--", readfds, writefds, NULL);
|
||||||
if (errno != ENOENT)
|
if (errno != ENOENT)
|
||||||
{
|
{
|
||||||
|
@ -149,17 +148,18 @@ CBlenderConnection::CBlenderConnection(bool silenceBlender)
|
||||||
|
|
||||||
/* Handle first response */
|
/* Handle first response */
|
||||||
char lineBuf[256];
|
char lineBuf[256];
|
||||||
readLine(lineBuf, sizeof(lineBuf));
|
_readLine(lineBuf, sizeof(lineBuf));
|
||||||
if (!strcmp(lineBuf, "NOLAUNCH"))
|
if (!strcmp(lineBuf, "NOLAUNCH"))
|
||||||
{
|
{
|
||||||
closePipe();
|
_closePipe();
|
||||||
throw std::runtime_error("Unable to launch blender");
|
throw std::runtime_error("Unable to launch blender");
|
||||||
}
|
}
|
||||||
else if (!strcmp(lineBuf, "NOBLENDER"))
|
else if (!strcmp(lineBuf, "NOBLENDER"))
|
||||||
{
|
{
|
||||||
closePipe();
|
_closePipe();
|
||||||
if (blenderBin)
|
if (blenderBin)
|
||||||
throw std::runtime_error("Unable to find blender at '" + std::string(blenderBin) + "' or '" +
|
throw std::runtime_error("Unable to find blender at '" +
|
||||||
|
std::string(blenderBin) + "' or '" +
|
||||||
std::string(DEFAULT_BLENDER_BIN) + "'");
|
std::string(DEFAULT_BLENDER_BIN) + "'");
|
||||||
else
|
else
|
||||||
throw std::runtime_error("Unable to find blender at '" +
|
throw std::runtime_error("Unable to find blender at '" +
|
||||||
|
@ -167,32 +167,76 @@ CBlenderConnection::CBlenderConnection(bool silenceBlender)
|
||||||
}
|
}
|
||||||
else if (!strcmp(lineBuf, "NOADDON"))
|
else if (!strcmp(lineBuf, "NOADDON"))
|
||||||
{
|
{
|
||||||
closePipe();
|
_closePipe();
|
||||||
throw std::runtime_error("HECL addon not installed within blender");
|
throw std::runtime_error("HECL addon not installed within blender");
|
||||||
}
|
}
|
||||||
else if (strcmp(lineBuf, "READY"))
|
else if (strcmp(lineBuf, "READY"))
|
||||||
{
|
{
|
||||||
closePipe();
|
_closePipe();
|
||||||
throw std::runtime_error("read '" + std::string(lineBuf) + "' from blender; expected 'READY'");
|
throw std::runtime_error("read '" + std::string(lineBuf) +
|
||||||
|
"' from blender; expected 'READY'");
|
||||||
}
|
}
|
||||||
writeLine("ACK");
|
_writeLine("ACK");
|
||||||
|
|
||||||
writeLine("HELLOBLENDER!!");
|
|
||||||
readLine(lineBuf, sizeof(lineBuf));
|
|
||||||
printf("%s\n", lineBuf);
|
|
||||||
quitBlender();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlenderConnection::~CBlenderConnection()
|
CBlenderConnection::~CBlenderConnection()
|
||||||
{
|
{
|
||||||
closePipe();
|
_closePipe();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlenderConnection::openBlend(const std::string& path)
|
||||||
|
{
|
||||||
|
_writeLine(("OPEN" + path).c_str());
|
||||||
|
char lineBuf[256];
|
||||||
|
_readLine(lineBuf, sizeof(lineBuf));
|
||||||
|
if (!strcmp(lineBuf, "FINISHED"))
|
||||||
|
{
|
||||||
|
m_loadedBlend = path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlenderConnection::cookBlend(std::function<void*(uint32_t)> bufGetter,
|
||||||
|
const std::string& expectedType,
|
||||||
|
const std::string& platform,
|
||||||
|
bool bigEndian)
|
||||||
|
{
|
||||||
|
char lineBuf[256];
|
||||||
|
char reqLine[512];
|
||||||
|
snprintf(reqLine, 512, "COOK %s %c", platform.c_str(), bigEndian?'>':'<');
|
||||||
|
_writeLine(reqLine);
|
||||||
|
_readLine(lineBuf, sizeof(lineBuf));
|
||||||
|
if (strcmp(expectedType.c_str(), lineBuf))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("expected '" + m_loadedBlend +
|
||||||
|
"' to contain " + expectedType +
|
||||||
|
" not " + lineBuf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_writeLine("ACK");
|
||||||
|
|
||||||
|
for (_readLine(lineBuf, sizeof(lineBuf));
|
||||||
|
!strcmp("BUF", lineBuf);
|
||||||
|
_readLine(lineBuf, sizeof(lineBuf)))
|
||||||
|
{
|
||||||
|
uint32_t sz;
|
||||||
|
_readBuf(&sz, 4);
|
||||||
|
void* buf = bufGetter(sz);
|
||||||
|
_readBuf(buf, sz);
|
||||||
|
}
|
||||||
|
if (!strcmp("SUCCESS", lineBuf))
|
||||||
|
return true;
|
||||||
|
else if (!strcmp("EXCEPTION", lineBuf))
|
||||||
|
throw std::runtime_error("blender script exception");
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBlenderConnection::quitBlender()
|
void CBlenderConnection::quitBlender()
|
||||||
{
|
{
|
||||||
writeLine("QUIT");
|
_writeLine("QUIT");
|
||||||
char lineBuf[256];
|
char lineBuf[256];
|
||||||
readLine(lineBuf, sizeof(lineBuf));
|
_readLine(lineBuf, sizeof(lineBuf));
|
||||||
printf("%s\n", lineBuf);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
class CBlenderConnection
|
class CBlenderConnection
|
||||||
{
|
{
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -19,15 +22,26 @@ class CBlenderConnection
|
||||||
int m_readpipe[2];
|
int m_readpipe[2];
|
||||||
int m_writepipe[2];
|
int m_writepipe[2];
|
||||||
#endif
|
#endif
|
||||||
size_t readLine(char* buf, size_t bufSz);
|
std::string m_loadedBlend;
|
||||||
size_t writeLine(const char* buf);
|
size_t _readLine(char* buf, size_t bufSz);
|
||||||
size_t readBuf(char* buf, size_t len);
|
size_t _writeLine(const char* buf);
|
||||||
size_t writeBuf(const char* buf, size_t len);
|
size_t _readBuf(char* buf, size_t len);
|
||||||
void closePipe();
|
size_t _writeBuf(const char* buf, size_t len);
|
||||||
|
void _closePipe();
|
||||||
public:
|
public:
|
||||||
CBlenderConnection(bool silenceBlender=false);
|
CBlenderConnection(bool silenceBlender=false);
|
||||||
~CBlenderConnection();
|
~CBlenderConnection();
|
||||||
|
|
||||||
|
bool openBlend(const std::string& path);
|
||||||
|
enum CookPlatform
|
||||||
|
{
|
||||||
|
CP_MODERN = 0,
|
||||||
|
CP_GX = 1,
|
||||||
|
};
|
||||||
|
bool cookBlend(std::function<void*(uint32_t)> bufGetter,
|
||||||
|
const std::string& expectedType,
|
||||||
|
const std::string& platform,
|
||||||
|
bool bigEndian=false);
|
||||||
void quitBlender();
|
void quitBlender();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,15 @@ bl_info = {
|
||||||
"category": "System"}
|
"category": "System"}
|
||||||
|
|
||||||
# Package import
|
# Package import
|
||||||
from . import hmdl
|
from . import hmdl, sact
|
||||||
import bpy, os, sys
|
import bpy, os, sys
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
|
|
||||||
# Appendable list allowing external addons to register additional resource types
|
# Appendable list allowing external addons to register additional resource types
|
||||||
hecl_export_types = [
|
hecl_typeS = [
|
||||||
('NONE', "None", "Active scene not using HECL", None, None),
|
('NONE', "None", "Active scene not using HECL", None, None),
|
||||||
('MESH', "Mesh", "Active scene represents an HMDL Mesh", hmdl.panel_draw, hmdl.cook)]
|
('MESH', "Mesh", "Active scene represents an HMDL Mesh", hmdl.draw, hmdl.cook),
|
||||||
|
('ACTOR', "Actor", "Active scene represents a HECL Actor", sact.draw, sact.cook)]
|
||||||
|
|
||||||
# Main Scene Panel
|
# Main Scene Panel
|
||||||
class hecl_scene_panel(bpy.types.Panel):
|
class hecl_scene_panel(bpy.types.Panel):
|
||||||
|
@ -37,46 +38,67 @@ class hecl_scene_panel(bpy.types.Panel):
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
type_row = layout.row(align=True)
|
type_row = layout.row(align=True)
|
||||||
type_row.prop_menu_enum(context.scene, 'hecl_export_type', text='Export Type')
|
type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type')
|
||||||
|
|
||||||
for exp_type in hecl_export_types:
|
for exp_type in hecl_typeS:
|
||||||
if exp_type[0] == context.scene.hecl_export_type and callable(exp_type[3]):
|
if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]):
|
||||||
exp_type[3](self, context)
|
exp_type[3](self.layout, context)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# Blender-selected polymorphism
|
# Blender-selected polymorphism cook
|
||||||
def do_package(writefd, platform_type, endian_char):
|
def do_cook(writebuf, platform_type, endian_char):
|
||||||
for tp in hecl_export_types:
|
for tp in hecl_typeS:
|
||||||
if tp[0] == bpy.context.scene.hecl_export_type:
|
if tp[0] == bpy.context.scene.hecl_type:
|
||||||
if callable(tp[4]):
|
if callable(tp[4]):
|
||||||
tp[4](writefd, platform_type, endian_char)
|
return tp[4](writefd, platform_type, endian_char)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Blender export-type registration
|
# Blender export-type registration
|
||||||
def register_export_type_enum():
|
def register_export_type_enum():
|
||||||
bpy.types.Scene.hecl_export_type = bpy.props.EnumProperty(items=
|
bpy.types.Scene.hecl_type = bpy.props.EnumProperty(items=
|
||||||
[tp[:3] for tp in hecl_export_types],
|
[tp[:3] for tp in hecl_typeS],
|
||||||
name="HECL Export Type",
|
name="HECL Export Type",
|
||||||
description="Selects how active scene is exported by HECL")
|
description="Selects how active scene is exported by HECL")
|
||||||
|
|
||||||
# Function for external addons to register export types with HECL
|
# Function for external addons to register export types with HECL
|
||||||
def add_export_type(type_tuple):
|
def add_export_type(type_tuple):
|
||||||
type_tup = tuple(type_tuple)
|
type_tup = tuple(type_tuple)
|
||||||
for tp in hecl_export_types:
|
for tp in hecl_typeS:
|
||||||
if tp[0] == type_tup[0]:
|
if tp[0] == type_tup[0]:
|
||||||
raise RuntimeError("Type already registered with HECL")
|
raise RuntimeError("Type already registered with HECL")
|
||||||
hecl_export_types.append(type_tup)
|
hecl_types.append(type_tup)
|
||||||
register_export_type_enum()
|
register_export_type_enum()
|
||||||
|
|
||||||
|
# Shell command receiver (from HECL driver)
|
||||||
|
def command(cmdline, writepipeline, writepipebuf):
|
||||||
|
if cmdline[0] == b'COOK':
|
||||||
|
resource_type = bpy.context.scene.hecl_type.encode()
|
||||||
|
writepipeline(resource_type)
|
||||||
|
ackbytes = readpipeline()
|
||||||
|
if ackbytes != b'ACK':
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
result = do_cook(writepipebuf, cmdline[1].decode(), cmdline[2].decode())
|
||||||
|
if result == None or result == True:
|
||||||
|
writepipeline(b'SUCCESS')
|
||||||
|
else:
|
||||||
|
writepipeline(b'FAILURE')
|
||||||
|
except:
|
||||||
|
writepipeline(b'EXCEPTION')
|
||||||
|
|
||||||
|
|
||||||
# Registration
|
# Registration
|
||||||
def register():
|
def register():
|
||||||
hmdl.register()
|
|
||||||
bpy.utils.register_class(hecl_scene_panel)
|
|
||||||
register_export_type_enum()
|
register_export_type_enum()
|
||||||
|
hmdl.register()
|
||||||
|
sact.register()
|
||||||
|
bpy.utils.register_class(hecl_scene_panel)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
hmdl.unregister()
|
hmdl.unregister()
|
||||||
|
sact.unregister()
|
||||||
bpy.utils.unregister_class(hecl_scene_panel)
|
bpy.utils.unregister_class(hecl_scene_panel)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -528,4 +528,3 @@ class hmdl_mesh_operator(bpy.types.Operator):
|
||||||
context.window_manager.clipboard = str_out
|
context.window_manager.clipboard = str_out
|
||||||
self.report({'INFO'}, "Wrote mesh C to clipboard")
|
self.report({'INFO'}, "Wrote mesh C to clipboard")
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
|
@ -10,7 +10,6 @@ or have a new one established.
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
import bpy
|
import bpy
|
||||||
from . import hmdl_anim
|
|
||||||
|
|
||||||
class hmdl_skin:
|
class hmdl_skin:
|
||||||
|
|
||||||
|
@ -72,14 +71,14 @@ class hmdl_skin:
|
||||||
|
|
||||||
|
|
||||||
# Generate Rigging Info structure (call after all index-buffers generated)
|
# Generate Rigging Info structure (call after all index-buffers generated)
|
||||||
def generate_rigging_info(self, endian_char):
|
def generate_rigging_info(self, bone_dict, endian_char):
|
||||||
|
|
||||||
skin_entries = []
|
skin_entries = []
|
||||||
for bone_array in self.bone_arrays:
|
for bone_array in self.bone_arrays:
|
||||||
skin_bytes = bytearray()
|
skin_bytes = bytearray()
|
||||||
skin_bytes += struct.pack(endian_char + 'I', len(bone_array))
|
skin_bytes += struct.pack(endian_char + 'I', len(bone_array))
|
||||||
for bone in bone_array:
|
for bone in bone_array:
|
||||||
skin_bytes += struct.pack(endian_char + 'i', hmdl_anim.hashbone(bone))
|
skin_bytes += struct.pack(endian_char + 'I', bone_dict[bone])
|
||||||
skin_entries.append(skin_bytes)
|
skin_entries.append(skin_bytes)
|
||||||
|
|
||||||
# Generate skinning data
|
# Generate skinning data
|
|
@ -23,10 +23,7 @@ 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 hmdl_shader
|
from . import HMDLShader, HMDLSkin, HMDLMesh, HMDLTxtr
|
||||||
from . import hmdl_skin
|
|
||||||
from . import hmdl_mesh
|
|
||||||
from . import hmdl_anim
|
|
||||||
|
|
||||||
def get_3d_context(object_):
|
def get_3d_context(object_):
|
||||||
window = bpy.context.window_manager.windows[0]
|
window = bpy.context.window_manager.windows[0]
|
||||||
|
@ -94,7 +91,9 @@ def generate_skeleton_info(armature, endian_char='<'):
|
||||||
# Takes a Blender 'Mesh' object (not the datablock)
|
# Takes a Blender 'Mesh' object (not the datablock)
|
||||||
# and performs a one-shot conversion process to HMDL; packaging
|
# and performs a one-shot conversion process to HMDL; packaging
|
||||||
# into the HECL data-pipeline and returning a hash once complete
|
# into the HECL data-pipeline and returning a hash once complete
|
||||||
def cook(writefd, platform_type, endian_char):
|
def cook(writebuffunc, platform, endianchar):
|
||||||
|
print('COOKING HMDL')
|
||||||
|
return True
|
||||||
mesh_obj = bpy.data.objects[bpy.context.scene.hecl_mesh_obj]
|
mesh_obj = bpy.data.objects[bpy.context.scene.hecl_mesh_obj]
|
||||||
if mesh_obj.type != 'MESH':
|
if mesh_obj.type != 'MESH':
|
||||||
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
|
||||||
|
@ -279,9 +278,7 @@ def cook(writefd, platform_type, endian_char):
|
||||||
|
|
||||||
return db_id, new_hash, final_data
|
return db_id, new_hash, final_data
|
||||||
|
|
||||||
|
def draw(layout, context):
|
||||||
def panel_draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
|
||||||
if not len(context.scene.hecl_mesh_obj):
|
if not len(context.scene.hecl_mesh_obj):
|
||||||
layout.label("Mesh not specified", icon='ERROR')
|
layout.label("Mesh not specified", icon='ERROR')
|
||||||
|
@ -301,8 +298,11 @@ def register():
|
||||||
bpy.types.Scene.hecl_mesh_obj = bpy.props.StringProperty(
|
bpy.types.Scene.hecl_mesh_obj = bpy.props.StringProperty(
|
||||||
name='HECL Mesh Object',
|
name='HECL Mesh Object',
|
||||||
description='Blender Mesh Object to export during HECL\'s cook process')
|
description='Blender Mesh Object to export during HECL\'s cook process')
|
||||||
bpy.utils.register_class(hmdl_shader.hecl_shader_operator)
|
bpy.types.Scene.hecl_actor_obj = bpy.props.StringProperty(
|
||||||
|
name='HECL Actor Object',
|
||||||
|
description='Blender Empty Object to export during HECL\'s cook process')
|
||||||
|
bpy.utils.register_class(HMDLShader.hecl_shader_operator)
|
||||||
pass
|
pass
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(hmdl_shader.hecl_shader_operator)
|
bpy.utils.unregister_class(HMDLShader.hecl_shader_operator)
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
'''
|
'''
|
||||||
HMDL Export Blender Addon
|
|
||||||
By Jack Andersen <jackoalan@gmail.com>
|
|
||||||
|
|
||||||
This file provides a means to encode animation key-channels
|
This file provides a means to encode animation key-channels
|
||||||
in an interleaved, sparse array for use by the runtime
|
in an interleaved, sparse array for use by the runtime
|
||||||
'''
|
'''
|
||||||
|
@ -11,10 +8,6 @@ import hashlib
|
||||||
import struct
|
import struct
|
||||||
import mathutils
|
import mathutils
|
||||||
|
|
||||||
# Hash bone name into truncated 28-bit integer
|
|
||||||
def hashbone(name):
|
|
||||||
return int.from_bytes(hashlib.sha1(name.encode()).digest()[:4], byteorder='big', signed=False) & 0xfffffff
|
|
||||||
|
|
||||||
# Regex RNA path matchers
|
# Regex RNA path matchers
|
||||||
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
|
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
|
||||||
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
|
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
|
|
@ -0,0 +1,211 @@
|
||||||
|
from . import SACTEvent
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
# Action update (if anything important changes)
|
||||||
|
def active_action_update(self, context):
|
||||||
|
if not bpy.app.background:
|
||||||
|
if context.scene.hecl_type == 'ACTOR' and context.scene.hecl_auto_select:
|
||||||
|
if SACTAction_load.poll(context):
|
||||||
|
bpy.ops.scene.SACTAction_load()
|
||||||
|
|
||||||
|
# Action type update
|
||||||
|
def action_type_update(self, context):
|
||||||
|
if not bpy.app.background:
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
active_action_update(self, context)
|
||||||
|
|
||||||
|
# Actor action class
|
||||||
|
class SACTAction(bpy.types.PropertyGroup):
|
||||||
|
name = bpy.props.StringProperty(name="Action Name")
|
||||||
|
action = bpy.props.StringProperty(name="Blender Action")
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.alignment = 'LEFT'
|
||||||
|
row.prop(actor_data, 'show_actions', text="Actions", icon='ACTION', emboss=False)
|
||||||
|
if actor_data.show_actions:
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.template_list("UI_UL_list", "SCENE_UL_SACTActions",
|
||||||
|
actor_data, 'actions', actor_data, 'active_action')
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("scene.sactaction_add", icon="ZOOMIN", text="")
|
||||||
|
col.operator("scene.sactaction_remove", icon="ZOOMOUT", text="")
|
||||||
|
|
||||||
|
if len(actor_data.actions) and actor_data.active_action >= 0:
|
||||||
|
action = actor_data.actions[actor_data.active_action]
|
||||||
|
|
||||||
|
# Load action operator
|
||||||
|
if not bpy.context.scene.hecl_auto_select:
|
||||||
|
layout.operator("scene.sactaction_load", icon='FILE_TICK', text="Load Action")
|
||||||
|
|
||||||
|
# Name edit field
|
||||||
|
layout.prop(action, 'name', text="Name")
|
||||||
|
|
||||||
|
layout.prop_search(action, 'action', bpy.data, 'actions', text="Action")
|
||||||
|
linked_action = None
|
||||||
|
if bpy.data.actions.find(action.action) != -1:
|
||||||
|
linked_action = bpy.data.actions[action.action]
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if linked_action is None:
|
||||||
|
layout.label("Source action not set", icon='ERROR')
|
||||||
|
else:
|
||||||
|
layout.prop(linked_action, 'hecl_index', text="Index")
|
||||||
|
layout.prop(linked_action, 'hecl_anim_props', text="Props")
|
||||||
|
layout.prop(linked_action, 'hecl_fps', text="Frame Rate")
|
||||||
|
layout.prop(context.scene, 'hecl_auto_remap', text="60-fps Remap")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Action 'add' operator
|
||||||
|
class SACTAction_add(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactaction_add"
|
||||||
|
bl_label = "New HECL Actor Action"
|
||||||
|
bl_description = "Add New HECL Actor Action to active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
action_name = 'ActorAction'
|
||||||
|
if action_name in actor_data.actions:
|
||||||
|
action_name = 'ActorAction.001'
|
||||||
|
action_idx = 1
|
||||||
|
while action_name in actor_data.actions:
|
||||||
|
action_idx += 1
|
||||||
|
action_name = 'ActorAction.{:0>3}'.format(action_idx)
|
||||||
|
action = actor_data.actions.add()
|
||||||
|
action.name = action_name
|
||||||
|
actor_data.active_action = len(actor_data.actions)-1
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Action 'remove' operator
|
||||||
|
class SACTAction_remove(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactaction_remove"
|
||||||
|
bl_label = "Remove HECL Actor Action"
|
||||||
|
bl_description = "Remove HECL Actor Action from active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
actor_data.active_action >= 0 and
|
||||||
|
len(actor_data.actions))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
actor_data.actions.remove(actor_data.active_action)
|
||||||
|
actor_data.active_action -= 1
|
||||||
|
if actor_data.active_action == -1:
|
||||||
|
actor_data.active_action = 0
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
# Action 'load' operator
|
||||||
|
class SACTAction_load(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactaction_load"
|
||||||
|
bl_label = "Load HECL Actor Action"
|
||||||
|
bl_description = "Loads Action for playback in active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
len(context.scene.hecl_sact_data.actions) and
|
||||||
|
context.scene.hecl_sact_data.active_action >= 0)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
if actor_data.active_action not in range(len(actor_data.actions)):
|
||||||
|
return {'CANCELLED'}
|
||||||
|
if actor_data.active_subtype not in range(len(actor_data.subtypes)):
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
action_data = actor_data.actions[actor_data.active_action]
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
|
||||||
|
# Refresh event markers
|
||||||
|
actor_event.clear_event_markers(actor_data, context)
|
||||||
|
actor_event.update_action_events(None)
|
||||||
|
actor_event.active_event_update(None, context)
|
||||||
|
|
||||||
|
# Clear animation data for all subtypes
|
||||||
|
for s in range(len(actor_data.subtypes)):
|
||||||
|
st = actor_data.subtypes[s]
|
||||||
|
if st.linked_armature in bpy.data.objects:
|
||||||
|
am = bpy.data.objects[st.linked_armature]
|
||||||
|
am.animation_data_clear()
|
||||||
|
|
||||||
|
# Set single action into armature
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
armature_obj = bpy.data.objects[subtype.linked_armature]
|
||||||
|
if action_data.action in bpy.data.actions:
|
||||||
|
action_obj =\
|
||||||
|
bpy.data.actions[action_data.action]
|
||||||
|
armature_obj.animation_data_clear()
|
||||||
|
armature_obj.animation_data_create()
|
||||||
|
armature_obj.animation_data.action = action_obj
|
||||||
|
|
||||||
|
# Time remapping
|
||||||
|
if context.scene.hecl_auto_remap:
|
||||||
|
bpy.context.scene.render.fps = 60
|
||||||
|
bpy.context.scene.render.frame_map_old = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.render.frame_map_new = 60
|
||||||
|
bpy.context.scene.frame_start = 2
|
||||||
|
bpy.context.scene.frame_end = action_obj.frame_range[1] * (60 / action_obj.hecl_fps)
|
||||||
|
else:
|
||||||
|
bpy.context.scene.render.fps = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.render.frame_map_old = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.render.frame_map_new = action_obj.hecl_fps
|
||||||
|
bpy.context.scene.frame_start = 1
|
||||||
|
bpy.context.scene.frame_end = action_obj.frame_range[1]
|
||||||
|
|
||||||
|
# Events
|
||||||
|
#actor_event.clear_action_events(self, context, actor_data)
|
||||||
|
#actor_event.load_action_events(self, context, action_obj, 0)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
else:
|
||||||
|
armature_obj.animation_data_clear()
|
||||||
|
self.report({'WARNING'}, "Unable to load action; check HECL panel")
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.report({'WARNING'}, "Unable to load armature; check HECL panel")
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.types.Action.hecl_fps = bpy.props.IntProperty(name="HECL Actor Sub-action Frame-rate",
|
||||||
|
description="Frame-rate at which action is authored; to be interpolated at 60-fps by runtime",
|
||||||
|
min=1, max=60, default=30,
|
||||||
|
update=active_action_update)
|
||||||
|
bpy.types.Action.hecl_anim_props = bpy.props.StringProperty(name="Animation Metadata")
|
||||||
|
bpy.types.Action.hecl_index = bpy.props.IntProperty(name="HECL Actor Action Index")
|
||||||
|
bpy.utils.register_class(SACTAction)
|
||||||
|
bpy.utils.register_class(SACTAction_add)
|
||||||
|
bpy.utils.register_class(SACTAction_load)
|
||||||
|
bpy.utils.register_class(SACTAction_remove)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SACTAction)
|
||||||
|
bpy.utils.unregister_class(SACTAction_add)
|
||||||
|
bpy.utils.unregister_class(SACTAction_load)
|
||||||
|
bpy.utils.unregister_class(SACTAction_remove)
|
|
@ -0,0 +1,444 @@
|
||||||
|
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.action in bpy.data.actions:
|
||||||
|
action_obj =\
|
||||||
|
bpy.data.actions[action_data.action]
|
||||||
|
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]
|
||||||
|
if action_data.active_subaction in range(len(action_data.subactions)):
|
||||||
|
subaction_data = action_data.subactions[action_data.active_subaction]
|
||||||
|
for marker in context.scene.timeline_markers:
|
||||||
|
if marker.name.startswith('hecl_'):
|
||||||
|
blend_action = bpy.data.actions[subaction_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)
|
|
@ -0,0 +1,184 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
# Subtype update (if anything important changes)
|
||||||
|
def active_subtype_update(self, context):
|
||||||
|
if context.scene.hecl_type == 'ACTOR' and context.scene.hecl_auto_select:
|
||||||
|
if SACTSubtype_load.poll(context):
|
||||||
|
bpy.ops.scene.SACTSubtype_load()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Actor subtype class
|
||||||
|
class SACTSubtype(bpy.types.PropertyGroup):
|
||||||
|
name = bpy.props.StringProperty(name="Actor Mesh Name")
|
||||||
|
linked_armature = bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
|
||||||
|
linked_mesh = bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
|
||||||
|
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.alignment = 'LEFT'
|
||||||
|
row.prop(actor_data, 'show_subtypes', text="Subtypes", icon='MESH_DATA', emboss=False)
|
||||||
|
if actor_data.show_subtypes:
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes",
|
||||||
|
actor_data, 'subtypes', actor_data, 'active_subtype')
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("scene.sactsubtype_add", icon="ZOOMIN", text="")
|
||||||
|
col.operator("scene.sactsubtype_remove", icon="ZOOMOUT", text="")
|
||||||
|
|
||||||
|
if len(actor_data.subtypes) and actor_data.active_subtype >= 0:
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
|
||||||
|
# Load subtype operator
|
||||||
|
if not bpy.context.scene.hecl_auto_select:
|
||||||
|
layout.operator("scene.sactsubtype_load", icon='FILE_TICK', text="Load Subtype")
|
||||||
|
|
||||||
|
# Name edit field
|
||||||
|
layout.prop(subtype, 'name', text="Name")
|
||||||
|
|
||||||
|
|
||||||
|
# Link external armature search
|
||||||
|
layout.prop_search(subtype, 'linked_armature', bpy.data, 'objects', text="Armature")
|
||||||
|
linked_armature = None
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
linked_armature = bpy.data.objects[subtype.linked_armature]
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if linked_armature is None:
|
||||||
|
layout.label("Source armature not set", icon='ERROR')
|
||||||
|
elif linked_armature is not None and linked_armature.type != 'ARMATURE':
|
||||||
|
layout.label("Source armature is not an 'ARMATURE'", icon='ERROR')
|
||||||
|
|
||||||
|
|
||||||
|
# Link external mesh search
|
||||||
|
layout.prop_search(subtype, 'linked_mesh', bpy.data, 'objects', text="Mesh")
|
||||||
|
linked_mesh = None
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
linked_mesh = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if linked_mesh is None:
|
||||||
|
layout.label("Source mesh not set", icon='ERROR')
|
||||||
|
elif linked_mesh.type != 'MESH':
|
||||||
|
layout.label("Source mesh not 'MESH'", icon='ERROR')
|
||||||
|
elif linked_armature is not None and linked_mesh not in linked_armature.children:
|
||||||
|
layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR')
|
||||||
|
elif linked_mesh.parent_type != 'ARMATURE':
|
||||||
|
layout.label("Source mesh not 'ARMATURE' parent type", icon='ERROR')
|
||||||
|
|
||||||
|
|
||||||
|
# Subtype 'add' operator
|
||||||
|
class SACTSubtype_add(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtype_add"
|
||||||
|
bl_label = "New HECL Actor Subtype"
|
||||||
|
bl_description = "Add New HECL Actor Subtype to active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
mesh_name = 'ActorMesh'
|
||||||
|
if mesh_name in actor_data.subtypes:
|
||||||
|
mesh_name = 'ActorMesh.001'
|
||||||
|
mesh_idx = 1
|
||||||
|
while mesh_name in actor_data.subtypes:
|
||||||
|
mesh_idx += 1
|
||||||
|
mesh_name = 'ActorMesh.{:0>3}'.format(mesh_idx)
|
||||||
|
mesh = actor_data.subtypes.add()
|
||||||
|
mesh.name = mesh_name
|
||||||
|
actor_data.active_subtype = len(actor_data.subtypes)-1
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Subtype 'remove' operator
|
||||||
|
class SACTSubtype_remove(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtype_remove"
|
||||||
|
bl_label = "Remove HECL Actor Subtype"
|
||||||
|
bl_description = "Remove HECL Actor Subtype from active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
return (context.scene is not None and
|
||||||
|
not context.scene.library and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
actor_data.active_subtype >= 0 and
|
||||||
|
len(actor_data.subtypes))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
actor_data.subtypes.remove(actor_data.active_subtype)
|
||||||
|
actor_data.active_subtype -= 1
|
||||||
|
if actor_data.active_subtype == -1:
|
||||||
|
actor_data.active_subtype = 0
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
# Subtype 'load' operator
|
||||||
|
class SACTSubtype_load(bpy.types.Operator):
|
||||||
|
bl_idname = "scene.sactsubtype_load"
|
||||||
|
bl_label = "Load HECL Actor Subtype"
|
||||||
|
bl_description = "Loads Subtype for viewing in active scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.scene is not None and
|
||||||
|
context.scene.hecl_type == 'ACTOR' and
|
||||||
|
len(context.scene.hecl_sact_data.subtypes) and
|
||||||
|
context.scene.hecl_sact_data.active_subtype >= 0)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
actor_data = context.scene.hecl_sact_data
|
||||||
|
subtype = actor_data.subtypes[actor_data.active_subtype]
|
||||||
|
|
||||||
|
# Armature
|
||||||
|
linked_armature = None
|
||||||
|
if subtype.linked_armature in bpy.data.objects:
|
||||||
|
linked_armature = bpy.data.objects[subtype.linked_armature]
|
||||||
|
else:
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# Hide armature children
|
||||||
|
for object in linked_armature.children:
|
||||||
|
if object.name in context.scene.objects:
|
||||||
|
object.hide = True
|
||||||
|
|
||||||
|
# Hide all meshes
|
||||||
|
for mesh_name in actor_data.subtypes:
|
||||||
|
if mesh_name.linked_mesh in bpy.data.objects:
|
||||||
|
mesh = bpy.data.objects[mesh_name.linked_mesh]
|
||||||
|
if mesh.name in context.scene.objects:
|
||||||
|
mesh.hide = True
|
||||||
|
|
||||||
|
# Show only the chosen subtype
|
||||||
|
if subtype.linked_mesh in bpy.data.objects:
|
||||||
|
mesh_obj = bpy.data.objects[subtype.linked_mesh]
|
||||||
|
mesh_obj.hide = False
|
||||||
|
if mesh_obj != linked_armature:
|
||||||
|
mesh_obj.parent = linked_armature
|
||||||
|
mesh_obj.parent_type = 'ARMATURE'
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
# Registration
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(SACTSubtype)
|
||||||
|
bpy.utils.register_class(SACTSubtype_add)
|
||||||
|
bpy.utils.register_class(SACTSubtype_remove)
|
||||||
|
bpy.utils.register_class(SACTSubtype_load)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SACTSubtype)
|
||||||
|
bpy.utils.unregister_class(SACTSubtype_add)
|
||||||
|
bpy.utils.unregister_class(SACTSubtype_remove)
|
||||||
|
bpy.utils.unregister_class(SACTSubtype_load)
|
|
@ -0,0 +1,253 @@
|
||||||
|
from . import SACTSubtype, SACTAction, SACTEvent, ANIM
|
||||||
|
from .. import hmdl
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import re
|
||||||
|
import os.path
|
||||||
|
import posixpath
|
||||||
|
import struct
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
# Actor data class
|
||||||
|
class SACTData(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
|
subtypes =\
|
||||||
|
bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List")
|
||||||
|
active_subtype =\
|
||||||
|
bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
|
||||||
|
show_subtypes =\
|
||||||
|
bpy.props.BoolProperty()
|
||||||
|
|
||||||
|
actions =\
|
||||||
|
bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List")
|
||||||
|
active_action =\
|
||||||
|
bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
|
||||||
|
show_actions =\
|
||||||
|
bpy.props.BoolProperty()
|
||||||
|
|
||||||
|
show_events =\
|
||||||
|
bpy.props.BoolProperty()
|
||||||
|
|
||||||
|
# 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]
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
# Cook
|
||||||
|
def cook(writebuffunc, platform, endianchar):
|
||||||
|
print('COOKING SACT')
|
||||||
|
|
||||||
|
|
||||||
|
# Panel draw
|
||||||
|
def draw(layout, context):
|
||||||
|
SACTSubtype.draw(layout.box(), context)
|
||||||
|
SACTAction.draw(layout.box(), context)
|
||||||
|
SACTEvent.draw(layout.box(), 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)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SACTData)
|
||||||
|
SACTSubtype.unregister()
|
||||||
|
SACTAction.unregister()
|
||||||
|
SACTEvent.unregister()
|
|
@ -19,9 +19,13 @@ DISTFILES += \
|
||||||
$$PWD/blendershell.py \
|
$$PWD/blendershell.py \
|
||||||
$$PWD/addon/__init__.py \
|
$$PWD/addon/__init__.py \
|
||||||
$$PWD/addon/hmdl/__init__.py \
|
$$PWD/addon/hmdl/__init__.py \
|
||||||
$$PWD/addon/hmdl/hmdl_anim.py \
|
$$PWD/addon/hmdl/HMDLMesh.py \
|
||||||
$$PWD/addon/hmdl/hmdl_mesh.py \
|
$$PWD/addon/hmdl/HMDLShader.py \
|
||||||
$$PWD/addon/hmdl/hmdl_shader.py \
|
$$PWD/addon/hmdl/HMDLSkin.py \
|
||||||
$$PWD/addon/hmdl/hmdl_skin.py \
|
$$PWD/addon/hmdl/HMDLTxtr.py \
|
||||||
$$PWD/addon/hmdl/hmdl_txtr.py
|
$$PWD/addon/sact/__init__.py \
|
||||||
|
$$PWD/addon/sact/SACTAction.py \
|
||||||
|
$$PWD/addon/sact/SACTEvent.py \
|
||||||
|
$$PWD/addon/sact/SACTSubtype.py \
|
||||||
|
$$PWD/addon/sact/ANIM.py
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ if '--' not in sys.argv:
|
||||||
args = sys.argv[sys.argv.index('--')+1:]
|
args = sys.argv[sys.argv.index('--')+1:]
|
||||||
readfd = int(args[0])
|
readfd = int(args[0])
|
||||||
writefd = int(args[1])
|
writefd = int(args[1])
|
||||||
print('READ', readfd, 'WRITE', writefd)
|
|
||||||
|
|
||||||
def readpipeline():
|
def readpipeline():
|
||||||
retval = bytearray()
|
retval = bytearray()
|
||||||
|
@ -18,7 +17,11 @@ def readpipeline():
|
||||||
retval += ch
|
retval += ch
|
||||||
|
|
||||||
def writepipeline(linebytes):
|
def writepipeline(linebytes):
|
||||||
ch = os.write(writefd, linebytes + b'\n')
|
os.write(writefd, linebytes + b'\n')
|
||||||
|
|
||||||
|
def writepipebuf(linebytes):
|
||||||
|
writepipeline(b'BUF')
|
||||||
|
os.write(writefd, struct.pack('I', len(linebytes)) + linebytes)
|
||||||
|
|
||||||
def quitblender():
|
def quitblender():
|
||||||
writepipeline(b'QUITTING')
|
writepipeline(b'QUITTING')
|
||||||
|
@ -30,6 +33,9 @@ if 'hecl' not in bpy.context.user_preferences.addons:
|
||||||
writepipeline(b'NOADDON')
|
writepipeline(b'NOADDON')
|
||||||
bpy.ops.wm.quit_blender()
|
bpy.ops.wm.quit_blender()
|
||||||
|
|
||||||
|
# Make addon available to commands
|
||||||
|
import hecl
|
||||||
|
|
||||||
# Intro handshake
|
# Intro handshake
|
||||||
writepipeline(b'READY')
|
writepipeline(b'READY')
|
||||||
ackbytes = readpipeline()
|
ackbytes = readpipeline()
|
||||||
|
@ -40,16 +46,15 @@ if ackbytes != b'ACK':
|
||||||
while True:
|
while True:
|
||||||
cmdline = readpipeline().split(b' ')
|
cmdline = readpipeline().split(b' ')
|
||||||
|
|
||||||
if not len(cmdline) or cmdline[0] == b'QUIT':
|
if cmdline[0] == b'QUIT':
|
||||||
quitblender()
|
quitblender()
|
||||||
|
|
||||||
elif cmdline[0] == b'OPEN':
|
elif cmdline[0] == b'OPEN':
|
||||||
bpy.ops.wm.open_mainfile(filepath=cmdline[1].decode())
|
if 'FINISHED' in bpy.ops.wm.open_mainfile(filepath=cmdline[1].decode()):
|
||||||
writepipeline(b'SUCCESS')
|
writepipeline(b'FINISHED')
|
||||||
|
else:
|
||||||
elif cmdline[0] == b'TYPE':
|
writepipeline(b'CANCELLED')
|
||||||
objname = cmdline[1].decode()
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
writepipeline(b'RESP ' + cmdline[0])
|
hecl.command(cmdline, writepipeline, writepipebuf)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue