2021-01-04 20:45:33 +08:00
|
|
|
|
2021-01-31 05:41:24 +08:00
|
|
|
class NullObject extends OutlinerElement {
|
2021-01-04 20:45:33 +08:00
|
|
|
constructor(data, uuid) {
|
|
|
|
super(data, uuid);
|
|
|
|
|
|
|
|
for (var key in NullObject.properties) {
|
|
|
|
NullObject.properties[key].reset(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data) {
|
|
|
|
this.extend(data);
|
|
|
|
}
|
|
|
|
}
|
2021-09-18 04:32:53 +08:00
|
|
|
get origin() {
|
|
|
|
return this.from;
|
|
|
|
}
|
2021-01-04 20:45:33 +08:00
|
|
|
extend(object) {
|
|
|
|
for (var key in NullObject.properties) {
|
|
|
|
NullObject.properties[key].merge(this, object)
|
|
|
|
}
|
|
|
|
this.sanitizeName();
|
|
|
|
//Merge.boolean(this, object, 'export');
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
getUndoCopy() {
|
|
|
|
var copy = new NullObject(this)
|
|
|
|
copy.uuid = this.uuid
|
|
|
|
copy.type = this.type;
|
|
|
|
delete copy.parent;
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
getSaveCopy() {
|
|
|
|
let save = {};
|
|
|
|
for (var key in NullObject.properties) {
|
|
|
|
NullObject.properties[key].copy(this, save)
|
|
|
|
}
|
|
|
|
//save.export = this.export ? undefined : false;
|
|
|
|
save.uuid = this.uuid;
|
|
|
|
save.type = 'null_object';
|
|
|
|
return save;
|
|
|
|
}
|
|
|
|
init() {
|
|
|
|
if (this.parent instanceof Group == false) {
|
|
|
|
this.addTo(Group.selected)
|
|
|
|
}
|
|
|
|
super.init();
|
|
|
|
return this;
|
|
|
|
}
|
2021-11-21 19:07:43 +08:00
|
|
|
select(event, isOutlinerClick) {
|
|
|
|
super.select(event, isOutlinerClick);
|
|
|
|
if (Animator.open && Animation.selected) {
|
|
|
|
Animation.selected.getBoneAnimator(this).select(true);
|
|
|
|
}
|
|
|
|
}
|
2021-01-04 20:45:33 +08:00
|
|
|
flip(axis, center) {
|
|
|
|
var offset = this.from[axis] - center
|
|
|
|
this.from[axis] = center - offset;
|
|
|
|
// Name
|
|
|
|
if (axis == 0 && this.name.includes('right')) {
|
|
|
|
this.name = this.name.replace(/right/g, 'left').replace(/2$/, '');
|
|
|
|
} else if (axis == 0 && this.name.includes('left')) {
|
|
|
|
this.name = this.name.replace(/left/g, 'right').replace(/2$/, '');
|
|
|
|
}
|
|
|
|
this.createUniqueName();
|
|
|
|
return this;
|
|
|
|
}
|
2021-11-30 01:15:08 +08:00
|
|
|
getWorldCenter(with_animation) {
|
2021-09-15 23:50:46 +08:00
|
|
|
var pos = Reusable.vec1.set(0, 0, 0);
|
|
|
|
var q = Reusable.quat1.set(0, 0, 0, 1);
|
2021-01-04 20:45:33 +08:00
|
|
|
if (this.parent instanceof Group) {
|
|
|
|
THREE.fastWorldPosition(this.parent.mesh, pos);
|
|
|
|
this.parent.mesh.getWorldQuaternion(q);
|
2021-09-15 23:50:46 +08:00
|
|
|
var offset2 = Reusable.vec2.fromArray(this.parent.origin).applyQuaternion(q);
|
2021-01-04 20:45:33 +08:00
|
|
|
pos.sub(offset2);
|
|
|
|
}
|
2021-11-30 01:15:08 +08:00
|
|
|
var offset = ( with_animation ? Reusable.vec3.copy(this.mesh.position) : Reusable.vec3.fromArray(this.from) ).applyQuaternion(q);
|
2021-01-04 20:45:33 +08:00
|
|
|
pos.add(offset);
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NullObject.prototype.title = tl('data.null_object');
|
|
|
|
NullObject.prototype.type = 'null_object';
|
|
|
|
NullObject.prototype.icon = 'fa far fa-circle';
|
|
|
|
//NullObject.prototype.name_regex = 'a-z0-9_'
|
|
|
|
NullObject.prototype.movable = true;
|
|
|
|
NullObject.prototype.visibility = true;
|
|
|
|
NullObject.prototype.buttons = [
|
|
|
|
//Outliner.buttons.export,
|
|
|
|
Outliner.buttons.locked,
|
|
|
|
];
|
2021-12-03 20:32:33 +08:00
|
|
|
NullObject.prototype.needsUniqueName = true;
|
2021-01-04 20:45:33 +08:00
|
|
|
NullObject.prototype.menu = new Menu([
|
2021-11-27 04:30:12 +08:00
|
|
|
'set_ik_target',
|
2021-12-03 20:32:33 +08:00
|
|
|
{
|
|
|
|
id: 'lock_ik_target_rotation',
|
|
|
|
name: 'menu.null_object.lock_ik_target_rotation',
|
|
|
|
icon: null_object => null_object.lock_ik_target_rotation ? 'check_box' : 'check_box_outline_blank',
|
|
|
|
click(clicked_null_object) {
|
|
|
|
let value = !clicked_null_object.lock_ik_target_rotation;
|
2021-12-08 01:31:16 +08:00
|
|
|
let affected = NullObject.selected.filter(null_object => null_object.lock_ik_target_rotation != value);
|
2021-12-03 20:32:33 +08:00
|
|
|
Undo.initEdit({elements: affected});
|
|
|
|
affected.forEach(null_object => {
|
|
|
|
null_object.lock_ik_target_rotation = value;
|
|
|
|
})
|
|
|
|
Undo.finishEdit('Change null object lock ik target rotation option');
|
|
|
|
}
|
|
|
|
},
|
2021-11-27 04:30:12 +08:00
|
|
|
'_',
|
2021-06-15 05:42:11 +08:00
|
|
|
'group_elements',
|
|
|
|
'_',
|
2021-01-04 20:45:33 +08:00
|
|
|
'copy',
|
2021-06-15 05:42:11 +08:00
|
|
|
'paste',
|
|
|
|
'duplicate',
|
|
|
|
'_',
|
2021-01-04 20:45:33 +08:00
|
|
|
'rename',
|
|
|
|
'delete'
|
|
|
|
])
|
|
|
|
|
|
|
|
new Property(NullObject, 'string', 'name', {default: 'null_object'})
|
|
|
|
new Property(NullObject, 'vector', 'from')
|
2021-11-27 04:30:12 +08:00
|
|
|
new Property(NullObject, 'string', 'ik_target', {condition: () => Format.animation_mode});
|
2021-12-03 20:32:33 +08:00
|
|
|
new Property(NullObject, 'boolean', 'lock_ik_target_rotation')
|
2021-10-09 22:29:33 +08:00
|
|
|
new Property(NullObject, 'boolean', 'locked');
|
2021-06-24 23:48:25 +08:00
|
|
|
|
2021-07-08 20:30:32 +08:00
|
|
|
OutlinerElement.registerType(NullObject, 'null_object');
|
2021-01-04 20:45:33 +08:00
|
|
|
|
2021-11-21 19:07:43 +08:00
|
|
|
new NodePreviewController(NullObject, {
|
|
|
|
setup(element) {
|
|
|
|
NodePreviewController.prototype.setup(element);
|
|
|
|
element.mesh.fix_position = new THREE.Vector3();
|
|
|
|
},
|
|
|
|
updateTransform(element) {
|
|
|
|
NodePreviewController.prototype.updateTransform(element);
|
|
|
|
element.mesh.fix_position.copy(element.mesh.position);
|
|
|
|
}
|
|
|
|
})
|
2021-09-18 04:32:53 +08:00
|
|
|
|
2021-01-04 20:45:33 +08:00
|
|
|
BARS.defineActions(function() {
|
|
|
|
new Action('add_null_object', {
|
|
|
|
icon: 'far.fa-circle',
|
|
|
|
category: 'edit',
|
2021-02-01 03:31:45 +08:00
|
|
|
condition: () => Format.animation_mode,
|
2021-01-04 20:45:33 +08:00
|
|
|
click: function () {
|
|
|
|
var objs = []
|
|
|
|
Undo.initEdit({elements: objs, outliner: true});
|
|
|
|
var null_object = new NullObject().addTo(Group.selected||selected[0]).init();
|
2021-12-03 20:32:33 +08:00
|
|
|
null_object.select().createUniqueName();
|
2021-01-04 20:45:33 +08:00
|
|
|
objs.push(null_object);
|
2021-06-06 15:28:22 +08:00
|
|
|
Undo.finishEdit('Add null object');
|
2021-01-04 20:45:33 +08:00
|
|
|
Vue.nextTick(function() {
|
|
|
|
if (settings.create_rename.value) {
|
|
|
|
null_object.rename();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2021-11-27 04:30:12 +08:00
|
|
|
|
|
|
|
new Action('set_ik_target', {
|
|
|
|
icon: 'fa-paperclip',
|
|
|
|
category: 'edit',
|
|
|
|
condition: () => NullObject.selected.length,
|
|
|
|
children() {
|
|
|
|
let groups = [];
|
|
|
|
iterate(NullObject.selected[0].getParentArray());
|
|
|
|
|
|
|
|
function iterate(arr) {
|
|
|
|
arr.forEach(node => {
|
|
|
|
if (node instanceof Group) {
|
|
|
|
groups.push(node);
|
|
|
|
iterate(node.children);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return groups.map(group => {
|
|
|
|
return {
|
|
|
|
name: group.name,
|
|
|
|
id: group.name,
|
|
|
|
icon: group.name == NullObject.selected[0].ik_target ? 'radio_button_checked' : 'radio_button_unchecked',
|
|
|
|
click() {
|
2021-11-30 01:15:08 +08:00
|
|
|
Undo.initEdit({elements: NullObject.selected});
|
2021-11-27 04:30:12 +08:00
|
|
|
NullObject.selected.forEach(null_object => {
|
|
|
|
null_object.ik_target = group.name;
|
|
|
|
})
|
2021-11-30 01:15:08 +08:00
|
|
|
Undo.finishEdit('Set IK target');
|
2021-11-27 04:30:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
click(event) {
|
|
|
|
new Menu(this.children()).show(event.target);
|
|
|
|
}
|
|
|
|
})
|
2021-01-04 20:45:33 +08:00
|
|
|
})
|