WIP: world space copy/paste prototype #115675

Closed
Christoph Lendenfeld wants to merge 12 commits from ChrisLend/blender:world_space_copy into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 350 additions and 0 deletions

View File

@ -36,6 +36,7 @@ set(SRC
keyframing.cc
keyingsets.cc
time_scrub_ui.cc
world_space_copy.cc
anim_intern.h
)

View File

@ -97,6 +97,14 @@ void ANIM_OT_paste_driver_button(struct wmOperatorType *ot);
/** \} */
/** \name World space copy and paste
* \{ */
void ANIM_OT_world_space_copy(wmOperatorType *ot);
void ANIM_OT_world_space_paste(wmOperatorType *ot);
/** \} */
#ifdef __cplusplus
}
#endif

View File

@ -679,6 +679,9 @@ void ED_operatortypes_anim()
WM_operatortype_append(ANIM_OT_copy_driver_button);
WM_operatortype_append(ANIM_OT_paste_driver_button);
WM_operatortype_append(ANIM_OT_world_space_copy);
WM_operatortype_append(ANIM_OT_world_space_paste);
WM_operatortype_append(ANIM_OT_keyingset_button_add);
WM_operatortype_append(ANIM_OT_keyingset_button_remove);

View File

@ -0,0 +1,338 @@
#include "ANIM_keyframing.hh"
#include "BKE_appdir.h"
#include "BKE_context.hh"
#include "BKE_layer.h"
#include "BLI_math_matrix.h"
#include "BLI_math_rotation.h"
#include "BLI_math_vector_types.hh"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "ED_keyframing.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "WM_types.hh"
#include "anim_intern.h"
typedef struct CopyBuffer {
int name_length;
char *name; /* Object/Bone name. */
float *matrices; /* Array of 4x4 matrices. */
} CopyBuffer;
typedef struct Foo {
int object_count;
int start_frame;
int frame_count;
CopyBuffer *buffer;
} Foo;
/* Copy matrix to flat array. */
static void flatten_matrix(float source[4][4], float *target)
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
target[i * 4 + j] = source[i][j];
}
}
}
static void compose_matrix(float *source, float target[4][4])
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
target[i][j] = source[i * 4 + j];
}
}
}
static void free_foo(Foo *foo)
{
for (int id_index = 0; id_index < foo->object_count; id_index++) {
CopyBuffer buffer = foo->buffer[id_index];
if (buffer.matrices != nullptr) {
MEM_freeN(buffer.matrices);
}
if (buffer.name != nullptr) {
MEM_freeN(buffer.name);
}
}
if (foo->buffer != nullptr) {
MEM_freeN(foo->buffer);
}
}
static bool write_foo(Foo *foo)
{
char filepath[FILE_MAX];
const char *cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, nullptr);
BLI_path_join(filepath, sizeof(filepath), cfgdir, "world_space_buffer");
FILE *f = fopen(filepath, "wb");
fwrite(&foo->start_frame, sizeof(int), 1, f);
fwrite(&foo->frame_count, sizeof(int), 1, f);
fwrite(&foo->object_count, sizeof(int), 1, f);
for (int id_index = 0; id_index < foo->object_count; id_index++) {
CopyBuffer *buffer = &foo->buffer[id_index];
fwrite(&buffer->name_length, sizeof(int), 1, f);
fwrite(buffer->name, sizeof(char), buffer->name_length + 1, f);
fwrite(buffer->matrices, sizeof(float), foo->frame_count * 16, f);
}
fclose(f);
return true;
}
static bool read_foo(Foo *foo)
{
char filepath[FILE_MAX];
const char *cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, nullptr);
BLI_path_join(filepath, sizeof(filepath), cfgdir, "world_space_buffer");
FILE *f = fopen(filepath, "rb");
if (!f) {
return false;
}
fread(&foo->start_frame, sizeof(int), 1, f);
fread(&foo->frame_count, sizeof(int), 1, f);
fread(&foo->object_count, sizeof(int), 1, f);
CopyBuffer *copy_buffer = static_cast<CopyBuffer *>(
MEM_callocN(sizeof(CopyBuffer) * foo->object_count, "World Space Copy Buffer"));
foo->buffer = copy_buffer;
for (int id_index = 0; id_index < foo->object_count; id_index++) {
CopyBuffer *buffer = &foo->buffer[id_index];
size_t read_size;
read_size = fread(&buffer->name_length, sizeof(int), 1, f);
if (read_size != 1) {
return false;
}
buffer->name = static_cast<char *>(
MEM_callocN(sizeof(char) * buffer->name_length + 1, "World Space name"));
read_size = fread(buffer->name, sizeof(char), buffer->name_length + 1, f);
if (read_size != buffer->name_length + 1) {
return false;
}
buffer->matrices = static_cast<float *>(
MEM_callocN(sizeof(float) * foo->frame_count * 16, "Copy Buffer Frame Values"));
read_size = fread(buffer->matrices, sizeof(float), foo->frame_count * 16, f);
if (read_size != foo->frame_count * 16) {
return false;
}
}
fclose(f);
return true;
}
static int world_space_copy_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
if (ob == nullptr) {
return OPERATOR_CANCELLED;
}
/* TODO copy selection instead of hardcoding active. */
ID *ids = &ob->id;
const int selected_ids = 1;
int range[2];
RNA_int_get_array(op->ptr, "range", range);
Scene *scene = CTX_data_scene(C);
if (range[0] == 0 && range[1] == 0) {
range[0] = scene->r.sfra;
range[1] = scene->r.efra;
}
const int frame_count = range[1] - range[0];
if (frame_count <= 0) {
return OPERATOR_CANCELLED;
}
Foo foo;
foo.object_count = selected_ids;
foo.frame_count = frame_count;
foo.start_frame = range[0];
CopyBuffer *copy_buffer = static_cast<CopyBuffer *>(
MEM_callocN(sizeof(CopyBuffer) * selected_ids, "World Space Copy Buffer"));
foo.buffer = copy_buffer;
for (int i = 0; i < selected_ids; i++) {
copy_buffer[i].name_length = strlen(ids[i].name);
copy_buffer[i].name = BLI_strdup(ids[i].name);
copy_buffer[i].matrices = static_cast<float *>(
MEM_callocN(sizeof(float) * frame_count * 16, "Copy Buffer Frame Values"));
}
Depsgraph *depsgraph = DEG_graph_new(
CTX_data_main(C), scene, CTX_data_view_layer(C), DAG_EVAL_VIEWPORT);
DEG_graph_build_from_ids(depsgraph, &ids, selected_ids);
for (int frame_index = 0; frame_index < frame_count; frame_index++) {
const int frame = range[0] + frame_index;
DEG_evaluate_on_framechange(depsgraph, frame);
for (int id_index = 0; id_index < selected_ids; id_index++) {
CopyBuffer buffer = copy_buffer[id_index];
float *matrix = &buffer.matrices[frame_index * 16];
Object *eval_object = DEG_get_evaluated_object(depsgraph, ob);
flatten_matrix(eval_object->object_to_world, matrix);
}
}
write_foo(&foo);
free_foo(&foo);
DEG_graph_free(depsgraph);
return OPERATOR_FINISHED;
}
void ANIM_OT_world_space_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy World Space";
ot->idname = "ANIM_OT_world_space_copy";
ot->description = "foo";
ot->exec = world_space_copy_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int_array(
ot->srna, "range", 2, nullptr, -INT_MAX, INT_MAX, "Frame Range", "", 0, INT_MAX);
}
static void paste_to_object(Foo *foo, Object *ob, Depsgraph *depsgraph, Main *bmain)
{
ID *id = &ob->id;
PointerRNA ptr = RNA_id_pointer_create(id);
bAction *action = ED_id_action_ensure(bmain, id);
if (action == nullptr) {
return;
}
/* Need to reset this or we'll have an offset. */
// unit_m4(ob->parentinv);
DEG_graph_build_from_ids(depsgraph, &id, 1);
CopyBuffer buffer = foo->buffer[0];
for (int frame_index = 0; frame_index < foo->frame_count; frame_index++) {
const int frame = foo->start_frame + frame_index;
DEG_evaluate_on_framechange(depsgraph, frame);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
float matrix_world[4][4];
compose_matrix(&buffer.matrices[frame_index * 16], matrix_world);
Object *parent = ob->parent;
float parent_matrix[4][4];
if (parent != nullptr) {
Object *parent_eval = DEG_get_evaluated_object(depsgraph, parent);
copy_m4_m4(parent_matrix, parent_eval->world_to_object);
}
else {
copy_m4_m4(parent_matrix, ob_eval->world_to_object);
}
/* I assume all this parentinv stuff can be simplified. */
float inv_parentinv[4][4];
invert_m4_m4(inv_parentinv, ob_eval->parentinv);
float whatever_that_matrix_is[4][4];
mul_m4_m4m4(whatever_that_matrix_is, inv_parentinv, parent_matrix);
float matrix_local[4][4];
mul_m4_m4m4(matrix_local, whatever_that_matrix_is, matrix_world);
blender::Array<float> location = {0, 0, 0};
float rot[3][3];
blender::Array<float> scale = {1, 1, 1};
mat4_to_loc_rot_size(location.data(), rot, scale.data(), matrix_local);
std::string rotation_path;
blender::Array<float> rotation;
if (ob->rotmode == ROT_MODE_QUAT) {
rotation_path = "rotation_quaternion";
rotation.reinitialize(4);
mat3_normalized_to_quat_fast(rotation.data(), rot);
}
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
rotation_path = "rotation_axis_angle";
rotation.reinitialize(4);
mat3_normalized_to_axis_angle(rotation.data(), &rotation[3], rot);
}
else {
rotation_path = "rotation_euler";
rotation.reinitialize(3);
mat3_normalized_to_eulO(rotation.data(), ob->rotmode, rot);
}
/* TODO: this can be faster by building a BezTriple array and merging it with existing keys. */
blender::animrig::insert_key_action(bmain,
action,
&ptr,
"location",
frame,
location.as_span(),
INSERTKEY_NOFLAGS,
BEZT_KEYTYPE_KEYFRAME);
blender::animrig::insert_key_action(bmain,
action,
&ptr,
rotation_path,
frame,
rotation.as_span(),
INSERTKEY_NOFLAGS,
BEZT_KEYTYPE_KEYFRAME);
blender::animrig::insert_key_action(bmain,
action,
&ptr,
"scale",
frame,
scale.as_span(),
INSERTKEY_NOFLAGS,
BEZT_KEYTYPE_KEYFRAME);
}
uint flags = ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION;
DEG_id_tag_update(id, flags);
// unit_m4(ob->parentinv);
}
static int world_space_paste_exec(bContext *C, wmOperator *op)
{
Foo foo;
const bool read_success = read_foo(&foo);
if (!read_success) {
free_foo(&foo);
return OPERATOR_CANCELLED;
}
Depsgraph *depsgraph = DEG_graph_new(
CTX_data_main(C), CTX_data_scene(C), CTX_data_view_layer(C), DAG_EVAL_VIEWPORT);
if (foo.object_count == 1) {
Object *ob = CTX_data_active_object(C);
if (ob == nullptr) {
free_foo(&foo);
DEG_graph_free(depsgraph);
return OPERATOR_CANCELLED;
}
paste_to_object(&foo, ob, depsgraph, CTX_data_main(C));
}
free_foo(&foo);
DEG_graph_free(depsgraph);
return OPERATOR_FINISHED;
}
void ANIM_OT_world_space_paste(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Paste World Space";
ot->idname = "ANIM_OT_world_space_paste";
ot->description = "foo";
ot->exec = world_space_paste_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}