Collada: add bone collection support
Replace the import/export of armature layers with bone collections.
The Old Way:
- Export: Each bone would store which armature layer it was on.
- Import: All armature layers that contain at least a bone are shown.
The New Way:
- Export: Each armature contains a list of its bone collections,
including which one is active and which ones are visible.
- Export: Each bone stores which bone collection it is on.
- Import: the above data is simply used as-is.
Due to limitations of the current Collada importer code, each "extra"
tag can only occur once per Collada node. This means that it was
impossible to write a `<collection name="Bones">` tag for each bone
collection, as only one of those would actually be stored by the
importer for further processing. To work around this limitation, all
bone collection related tags store their values as newline-separated
strings. Example:
```
<node id="Armature">
<extra>
<technique profile="blender">
<collections sid="collections" type="string">Layer 1
Layer 3b
Group
That One Bone</collections>
<visible_collections sid="visible_collections" type="string">Layer 1
Layer 3b
That One Bone</visible_collections>
<active_collection sid="active_collection" type="string">That One Bone</active_collection>
</technique>
</extra>
</node>
```
This commit is contained in:
@@ -138,6 +138,7 @@ void ANIM_armature_bonecoll_name_set(struct bArmature *armature,
|
||||
struct BoneCollection *bcoll,
|
||||
const char *name);
|
||||
|
||||
void ANIM_bonecoll_show(struct BoneCollection *bcoll);
|
||||
void ANIM_bonecoll_hide(struct BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
@@ -214,10 +215,6 @@ inline bool ANIM_bonecoll_is_visible_actbone(const struct bArmature *armature)
|
||||
void ANIM_armature_bonecoll_show_all(struct bArmature *armature);
|
||||
void ANIM_armature_bonecoll_hide_all(struct bArmature *armature);
|
||||
|
||||
/* Only used by the Collada I/O code: */
|
||||
void ANIM_armature_enable_layers(struct bArmature *armature, const int layers);
|
||||
void ANIM_bone_set_layer_ebone(struct EditBone *ebone, int layer);
|
||||
|
||||
void ANIM_armature_bonecoll_show_from_bone(struct bArmature *armature, const struct Bone *bone);
|
||||
void ANIM_armature_bonecoll_show_from_ebone(struct bArmature *armature,
|
||||
const struct EditBone *ebone);
|
||||
|
||||
@@ -477,19 +477,6 @@ void ANIM_armature_bonecoll_hide_all(bArmature *armature)
|
||||
/* ********************************* */
|
||||
/* Armature Layers transitional API. */
|
||||
|
||||
void ANIM_armature_enable_layers(bArmature *armature, const int /*layers*/)
|
||||
{
|
||||
// TODO: reimplement properly.
|
||||
// armature->layer |= layers;
|
||||
ANIM_armature_bonecoll_show_all(armature);
|
||||
}
|
||||
|
||||
void ANIM_bone_set_layer_ebone(EditBone *ebone, const int layer)
|
||||
{
|
||||
// TODO: reimplement for bone collections.
|
||||
ebone->layer = layer;
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_assign_active(const bArmature *armature, EditBone *ebone)
|
||||
{
|
||||
if (armature->runtime.active_collection == nullptr) {
|
||||
|
||||
@@ -29,6 +29,42 @@
|
||||
#include "GeometryExporter.h"
|
||||
#include "SceneExporter.h"
|
||||
|
||||
void ArmatureExporter::add_bone_collections(Object *ob_arm, COLLADASW::Node &node)
|
||||
{
|
||||
bArmature *armature = (bArmature *)ob_arm->data;
|
||||
|
||||
/* Because our importer assumes that "extras" tags have a unique name, it's not posisble to
|
||||
* export a <bonecollection> element per bone collection. This is why all the names are stored in
|
||||
* one element, newline-separated. */
|
||||
|
||||
std::stringstream collection_stream;
|
||||
std::stringstream visible_stream;
|
||||
LISTBASE_FOREACH (const BoneCollection *, bcoll, &armature->collections) {
|
||||
collection_stream << bcoll->name << "\n";
|
||||
|
||||
if (bcoll->flags & BONE_COLLECTION_VISIBLE) {
|
||||
visible_stream << bcoll->name << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::string collection_names = collection_stream.str();
|
||||
if (collection_names.length() > 1) {
|
||||
collection_names.pop_back(); // Pop off the last \n.
|
||||
node.addExtraTechniqueParameter("blender", "collections", collection_names);
|
||||
}
|
||||
|
||||
std::string visible_names = visible_stream.str();
|
||||
if (visible_names.length() > 1) {
|
||||
visible_names.pop_back(); // Pop off the last \n.
|
||||
node.addExtraTechniqueParameter("blender", "visible_collections", visible_names);
|
||||
}
|
||||
|
||||
if (armature->runtime.active_collection) {
|
||||
node.addExtraTechniqueParameter(
|
||||
"blender", "active_collection", std::string(armature->active_collection_name));
|
||||
}
|
||||
}
|
||||
|
||||
void ArmatureExporter::add_armature_bones(Object *ob_arm,
|
||||
ViewLayer *view_layer,
|
||||
SceneExporter *se,
|
||||
@@ -154,8 +190,15 @@ void ArmatureExporter::add_bone_node(Bone *bone,
|
||||
node.addExtraTechniqueParameter("blender", "connect", true);
|
||||
}
|
||||
}
|
||||
std::string layers = BoneExtended::get_bone_layers(bone->layer);
|
||||
node.addExtraTechniqueParameter("blender", "layer", layers);
|
||||
|
||||
std::string collection_names = "";
|
||||
LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, &bone->runtime.collections) {
|
||||
collection_names += std::string(bcoll_ref->bcoll->name) + "\n";
|
||||
}
|
||||
if (collection_names.length() > 1) {
|
||||
collection_names.pop_back(); // Pop off the last \n.
|
||||
node.addExtraTechniqueParameter("blender", "", collection_names, "", "collections");
|
||||
}
|
||||
|
||||
bArmature *armature = (bArmature *)ob_arm->data;
|
||||
EditBone *ebone = bc_get_edit_bone(armature, bone->name);
|
||||
|
||||
@@ -50,6 +50,8 @@ class ArmatureExporter : public COLLADASW::LibraryControllers,
|
||||
{
|
||||
}
|
||||
|
||||
void add_bone_collections(Object *ob_arm, COLLADASW::Node &node);
|
||||
|
||||
/* write bone nodes */
|
||||
void add_armature_bones(Object *ob_arm,
|
||||
ViewLayer *view_layer,
|
||||
|
||||
@@ -140,12 +140,13 @@ int ArmatureImporter::create_bone(SkinInfo *skin,
|
||||
float loc[3], size[3], rot[3][3];
|
||||
BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(arm);
|
||||
BoneExtended &be = add_bone_extended(bone, node, totchild, layer_labels, extended_bones);
|
||||
int layer = be.get_bone_layers();
|
||||
if (layer) {
|
||||
ANIM_bone_set_layer_ebone(bone, layer);
|
||||
|
||||
for (const std::string &bcoll_name : be.get_bone_collections()) {
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_get_by_name(arm, bcoll_name.c_str());
|
||||
if (bcoll) {
|
||||
ANIM_armature_bonecoll_assign_editbone(bcoll, bone);
|
||||
}
|
||||
}
|
||||
/* Ensure that all populated bone layers are visible after import. */
|
||||
ANIM_armature_enable_layers(arm, layer);
|
||||
|
||||
float *tail = be.get_tail();
|
||||
int use_connect = be.get_use_connect();
|
||||
@@ -490,8 +491,6 @@ void ArmatureImporter::create_armature_bones(Main *bmain, std::vector<Object *>
|
||||
}
|
||||
|
||||
ED_armature_to_edit(armature);
|
||||
/* Layers are enabled according to imported bone set in create_bone(). */
|
||||
ANIM_armature_bonecoll_hide_all(armature);
|
||||
|
||||
create_bone(
|
||||
nullptr, node, nullptr, node->getChildNodes().getCount(), nullptr, armature, layer_labels);
|
||||
@@ -530,7 +529,6 @@ Object *ArmatureImporter::create_armature_bones(Main *bmain, SkinInfo &skin)
|
||||
* - add edit bones and head/tail properties using matrices and parent-child info
|
||||
* - exit edit mode
|
||||
* - set a sphere shape to leaf bones */
|
||||
|
||||
Object *ob_arm = nullptr;
|
||||
|
||||
/*
|
||||
@@ -1060,7 +1058,6 @@ BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone,
|
||||
|
||||
float tail[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
|
||||
float roll = 0;
|
||||
std::string layers;
|
||||
|
||||
et = etit->second;
|
||||
|
||||
@@ -1072,7 +1069,7 @@ BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone,
|
||||
has_connect = et->setData("connect", &connect_type);
|
||||
bool has_roll = et->setData("roll", &roll);
|
||||
|
||||
layers = et->setData("layer", layers);
|
||||
be->set_bone_collections(et->dataSplitString("collections"));
|
||||
|
||||
if (has_tail && !has_connect) {
|
||||
/* got a bone tail definition but no connect info -> bone is not connected */
|
||||
@@ -1080,7 +1077,6 @@ BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone,
|
||||
connect_type = 0;
|
||||
}
|
||||
|
||||
be->set_bone_layers(layers, layer_labels);
|
||||
if (has_tail) {
|
||||
be->set_tail(tail);
|
||||
}
|
||||
|
||||
@@ -622,7 +622,8 @@ std::vector<Object *> *DocumentImporter::write_node(COLLADAFW::Node *node,
|
||||
if ((geom_done + camera_done + lamp_done + controller_done + inst_done) < 1) {
|
||||
/* Check if Object is armature, by checking if immediate child is a JOINT node. */
|
||||
if (is_armature(node)) {
|
||||
ob = bc_add_object(bmain, sce, view_layer, OB_ARMATURE, name.c_str());
|
||||
ExtraTags *et = getExtraTags(node->getUniqueId());
|
||||
ob = bc_add_armature(node, et, bmain, sce, view_layer, OB_ARMATURE, name.c_str());
|
||||
}
|
||||
else {
|
||||
ob = bc_add_object(bmain, sce, view_layer, OB_EMPTY, nullptr);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
#include "ExtraTags.h"
|
||||
|
||||
@@ -110,3 +111,23 @@ std::string ExtraTags::setData(std::string tag, std::string &data)
|
||||
std::string tmp = asString(tag, &ok);
|
||||
return (ok) ? tmp : data;
|
||||
}
|
||||
|
||||
std::vector<std::string> ExtraTags::dataSplitString(const std::string &tag)
|
||||
{
|
||||
bool ok = false;
|
||||
const std::string value = asString(tag, &ok);
|
||||
if (!ok) {
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<std::string> values;
|
||||
|
||||
const std::regex newline_re("[^\\s][^\\r\\n]+");
|
||||
const std::sregex_token_iterator end;
|
||||
std::sregex_token_iterator iter(value.begin(), value.end(), newline_re);
|
||||
for (; iter != end; iter++) {
|
||||
values.push_back(*iter);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ class ExtraTags {
|
||||
bool setData(std::string tag, char *data);
|
||||
std::string setData(std::string tag, std::string &data);
|
||||
|
||||
/** Get a string from the data, and split it by newlines. */
|
||||
std::vector<std::string> dataSplitString(const std::string &tag);
|
||||
|
||||
/** Return true if the extra tags is for specified profile. */
|
||||
bool isProfile(std::string profile);
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ void SceneExporter::writeNode(Object *ob)
|
||||
|
||||
/* <instance_controller> */
|
||||
else if (ob->type == OB_ARMATURE) {
|
||||
arm_exporter->add_bone_collections(ob, colladaNode);
|
||||
arm_exporter->add_armature_bones(ob, view_layer, this, child_objects);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "COLLADAFWGeometry.h"
|
||||
#include "COLLADAFWMeshPrimitive.h"
|
||||
#include "COLLADAFWMeshVertexData.h"
|
||||
#include "COLLADAFWNode.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
@@ -48,6 +49,8 @@
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "ED_node.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
@@ -66,6 +69,7 @@
|
||||
|
||||
#include "BlenderContext.h"
|
||||
#include "ExportSettings.h"
|
||||
#include "ExtraTags.h"
|
||||
#include "collada_utils.h"
|
||||
|
||||
float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray &array, uint index)
|
||||
@@ -207,6 +211,41 @@ Object *bc_add_object(Main *bmain, Scene *scene, ViewLayer *view_layer, int type
|
||||
return ob;
|
||||
}
|
||||
|
||||
static void bc_add_armature_collections(COLLADAFW::Node *node,
|
||||
ExtraTags *node_extra_tags,
|
||||
bArmature *arm)
|
||||
{
|
||||
std::vector<std::string> collection_names = node_extra_tags->dataSplitString("collections");
|
||||
std::vector<std::string> visible_names = node_extra_tags->dataSplitString("visible_collections");
|
||||
std::set<std::string> visible_names_set(visible_names.begin(), visible_names.end());
|
||||
for (const std::string &name : collection_names) {
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_new(arm, name.c_str());
|
||||
if (visible_names_set.find(name) == visible_names_set.end()) {
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
}
|
||||
else {
|
||||
ANIM_bonecoll_show(bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
std::string active_name;
|
||||
active_name = node_extra_tags->setData("active_collection", active_name);
|
||||
ANIM_armature_bonecoll_active_name_set(arm, active_name.c_str());
|
||||
}
|
||||
|
||||
Object *bc_add_armature(COLLADAFW::Node *node,
|
||||
ExtraTags *node_extra_tags,
|
||||
Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
int type,
|
||||
const char *name)
|
||||
{
|
||||
Object *ob = bc_add_object(bmain, scene, view_layer, type, name);
|
||||
bc_add_armature_collections(node, node_extra_tags, reinterpret_cast<bArmature *>(ob->data));
|
||||
return ob;
|
||||
}
|
||||
|
||||
Mesh *bc_get_mesh_copy(BlenderContext &blender_context,
|
||||
Object *ob,
|
||||
BC_export_mesh_type export_mesh_type,
|
||||
@@ -500,7 +539,6 @@ BoneExtended::BoneExtended(EditBone *aBone)
|
||||
this->tail[2] = 0.0f;
|
||||
this->use_connect = -1;
|
||||
this->roll = 0;
|
||||
this->bone_layers = 0;
|
||||
|
||||
this->has_custom_tail = false;
|
||||
this->has_custom_roll = false;
|
||||
@@ -582,62 +620,13 @@ inline bool isInteger(const std::string &s)
|
||||
return (*p == 0);
|
||||
}
|
||||
|
||||
void BoneExtended::set_bone_layers(std::string layerString, std::vector<std::string> &layer_labels)
|
||||
void BoneExtended::set_bone_collections(std::vector<std::string> bone_collections)
|
||||
{
|
||||
std::stringstream ss(layerString);
|
||||
std::string layer;
|
||||
int pos;
|
||||
|
||||
while (ss >> layer) {
|
||||
|
||||
/* Blender uses numbers to specify layers. */
|
||||
if (isInteger(layer)) {
|
||||
pos = atoi(layer.c_str());
|
||||
if (pos >= 0 && pos < 32) {
|
||||
this->bone_layers = bc_set_layer(this->bone_layers, pos);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Layer uses labels (not supported by blender). Map to layer numbers: */
|
||||
pos = find(layer_labels.begin(), layer_labels.end(), layer) - layer_labels.begin();
|
||||
if (pos >= layer_labels.size()) {
|
||||
layer_labels.push_back(layer); /* Remember layer number for future usage. */
|
||||
}
|
||||
|
||||
if (pos > 31) {
|
||||
fprintf(stderr,
|
||||
"Too many layers in Import. Layer %s mapped to Blender layer 31\n",
|
||||
layer.c_str());
|
||||
pos = 31;
|
||||
}
|
||||
|
||||
/* If numeric layers and labeled layers are used in parallel (unlikely),
|
||||
* we get a potential mix-up. Just leave as is for now. */
|
||||
this->bone_layers = bc_set_layer(this->bone_layers, pos);
|
||||
}
|
||||
this->bone_collections = bone_collections;
|
||||
}
|
||||
|
||||
std::string BoneExtended::get_bone_layers(int bitfield)
|
||||
const std::vector<std::string> &BoneExtended::get_bone_collections()
|
||||
{
|
||||
std::string sep;
|
||||
int bit = 1u;
|
||||
|
||||
std::ostringstream ss;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (bit & bitfield) {
|
||||
ss << sep << i;
|
||||
sep = " ";
|
||||
}
|
||||
bit = bit << 1;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int BoneExtended::get_bone_layers()
|
||||
{
|
||||
/* ensure that the bone is in at least one bone layer! */
|
||||
return (bone_layers == 0) ? 1 : bone_layers;
|
||||
return this->bone_collections;
|
||||
}
|
||||
|
||||
void BoneExtended::set_use_connect(int use_connect)
|
||||
|
||||
@@ -59,6 +59,11 @@ typedef std::map<std::string, Image *> KeyImageMap;
|
||||
typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex *>> TexIndexTextureArrayMap;
|
||||
typedef std::set<Object *> BCObjectSet;
|
||||
|
||||
namespace COLLADAFW {
|
||||
class Node;
|
||||
}
|
||||
class ExtraTags;
|
||||
|
||||
extern void bc_update_scene(BlenderContext &blender_context, float ctime);
|
||||
|
||||
/* Action helpers */
|
||||
@@ -118,6 +123,13 @@ extern bool bc_validateConstraints(bConstraint *con);
|
||||
bool bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space = true);
|
||||
extern Object *bc_add_object(
|
||||
Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name);
|
||||
extern Object *bc_add_armature(COLLADAFW::Node *node,
|
||||
ExtraTags *node_extra_tags,
|
||||
Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
int type,
|
||||
const char *name);
|
||||
extern Mesh *bc_get_mesh_copy(BlenderContext &blender_context,
|
||||
Object *ob,
|
||||
BC_export_mesh_type export_mesh_type,
|
||||
@@ -362,7 +374,8 @@ class BoneExtended {
|
||||
float tail[3];
|
||||
float roll;
|
||||
|
||||
int bone_layers;
|
||||
std::vector<std::string> bone_collections;
|
||||
|
||||
int use_connect;
|
||||
bool has_custom_tail;
|
||||
bool has_custom_roll;
|
||||
@@ -379,9 +392,8 @@ class BoneExtended {
|
||||
void set_leaf_bone(bool state);
|
||||
bool is_leaf_bone();
|
||||
|
||||
void set_bone_layers(std::string layers, std::vector<std::string> &layer_labels);
|
||||
int get_bone_layers();
|
||||
static std::string get_bone_layers(int bitfield);
|
||||
void set_bone_collections(std::vector<std::string> bone_collections);
|
||||
const std::vector<std::string> &get_bone_collections();
|
||||
|
||||
void set_roll(float roll);
|
||||
bool has_roll();
|
||||
|
||||
Reference in New Issue
Block a user