opengl assimp skeletal node transformation - c

if i have a setup where in order to transform a bone, I have to:
node.transform = scale(node.transform, 2, 2, 2)
then
bone_transform = inverse:offset_matrix *
(node.parent.transform * node.transform) * (inverse:node.parent.transform) * offset_matrix
However this only works properly for bones that doesn't have any children (or the bottom-most in the node hierarchy)..
therefore having trouble when traversing through the bones part..
how would I do this correctly to get the correct bone_transform for all bones..
So if I want to transform any bone anywhere in the hierarchy (using node.transform), it would also adjust all its children bone_transforms correctly..
maybe something like:
mat4 transform = node.transform
while (node.parent != 0) {
if (node.is_bone) {
transform = node.parent.transform * node.transform
}
node = node.parent
}
or something with node.children recursively?

The offset-matrix is already the global transformation for your bone. It contains already all the transformation data from the root down to your current bone.
That is the reason that your code only works for bones without any parent nodes.

Related

Store values that are created in a loop outside of the loop in rust

I am working on a little parser that takes an S-Expression and transforms it into a tree data structure. Since trees are not so easy in Rust to do bidirectional I want to make it only one-directional (each node only knows it's children, not it's parents). To still be able to access nodes that have been created in the past, I want to store all nodes in a Vector and keep an index that tells me which node is where. E.g. a node would have a field parent_index, with the index of it's parent node in the vector, without needing to have a direct reference to it.
So, while I iterate over the String that is my s-expression, I am creating new nodes and want to push them into the vector, as well as assigning child relationships.
for clarifications an S-Expression can be this (w1 (w2 t1) (w3 t2)) or something like this (S (F (FIRSTWORD This) (SECONDWORD is)) (Z (THIRDWORD a) (FOURTHWORD sentence))), or just a terminal like Hello
A lot is still missing but my idea looks like this. My question now is: How can I create all these Trees inside the loop and store them into the vector outside of the loop?
It seems that the borrow checker has some probelems with this...
I am very grateful for any help! Thank you :)
My idea looks like this:
pub struct Tree{
pub root: String,
pub children: Vec<Tree>
}
fn parse_to_tree(mut s_expr: String) -> Tree{
// Vector for all nodes
let mut all_nodes:Vec<&Tree> = Vec::new();
// Index of node we are working on
let mut curr_node_index = 0;
while !s_expr.is_empty(){
// get first char from s expression
let mut c_c = s_expr.remove(0);
//Names: c_c: current_char, c_w: current:word
// only continue if we start with '(', otherwise Leaf
while c_c == '('{
let mut c_w = String::from("");
while c_c != ' '{
c_c = s_expr.remove(0);
c_w.push(c_c);
}
println!("{}", c_w);
let node = Tree{root: c_w, children: Vec::new()};
//Here I want to push the node by reference to the all_nodes_vector
all_nodes.push(&node); // HERE occurs the error: borrowed value does not live long enough...
if !all_nodes.is_empty(){
// Here I want to to push the new node as a child to the old current_node
all_nodes[curr_node_index].children.push(node);
}
curr_node_index += 1;
}
return Tree{root: s_expr, children: Vec::new()};
}
return Tree{root: s_expr, children: Vec::new()};
}
You cannot do that (safely): someone, somewhere, while building this tree, will have to hold both a node and the list of nodes that contains this node. And (at least) the reference to the node will be mutable (because we need to build it), causing a violation of the borrow rules.
The solution is simple: just give up on using references. Store all nodes in a vector, and only keep indices to this vector. This is easy, effective and idiomatic.

Are accessors of class properties useful?

Consider the following accessor methods of the property global_position of the class Node2D:
Vector2 global_position
Setter set_global_position(value)
Getter get_global_position()
But the property is not encapsulated as shown by this example:
tool
extends EditorScript
func _run() -> void:
var n = Node2D.new()
n.global_position = Vector2(100, 100)
print(n.global_position)
which produces:
* scene/2d/canvas_item.cpp:467 - Condition "!is_inside_tree()" is true. Returned: get_transform()
(100, 100)
Are those accessors useless?
They are NOT useless. They are useful… If the node is in the scene tree.
You can add the node to the scene tree with add_child or add_child_below_node.
I'm not sure what you mean by "not encapsulated". Just in case, I'll point out that you are NOT bypassing them.
When you use the property, you are using the getter and setter methdods. The property is a convenience for language binding. Thus, we could also say that you don't need the property, only the getter and setter.
You can see in the source for Node2D, that there is a _bind_methods function that sets all the properties and methods that are exposed to use. This is how global_position looks like:
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
That message you are getting is because you are using global_position on a node that is not on the scene tree. And, yes in that case, it is not useful. We can figure that out with a simple script:
extends Node2D
func _ready() -> void:
global_position = Vector2(200, 300)
var n = Node2D.new()
n.global_position = Vector2(100, 100)
print(n.global_position)
add_child(n)
print(n.global_position)
This outputs:
(100, 100)
(300, 400)
Thus, as you can see, it will have a different global position from what was set. And the difference depends on the position of the parent. Setting position would have the same effect. Thus setting global_position not useful here.
If you want to go deeper on what global_position does, we can have a look at the source of the getter and setter (part of the linked source for Node2D):
Point2 Node2D::get_global_position() const {
return get_global_transform().get_origin();
}
void Node2D::set_global_position(const Point2 &p_pos) {
Transform2D inv;
CanvasItem *pi = get_parent_item();
if (pi) {
inv = pi->get_global_transform().affine_inverse();
set_position(inv.xform(p_pos));
} else {
set_position(p_pos);
}
}
This is set_position, by the way (notice it writes pos):
void Node2D::set_position(const Point2 &p_pos) {
if (_xform_dirty)
((Node2D *)this)->_update_xform_values();
pos = p_pos;
_update_transform();
_change_notify("position");
}
And _update_transform (with conspicuous !is_inside_tree() check):
void Node2D::_update_transform() {
_mat.set_rotation_and_scale(angle, _scale);
_mat.elements[2] = pos;
VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
if (!is_inside_tree())
return;
_notify_transform();
}
Notice that _update_transform updates _mat based on pos.
What about get_global_transform? Not in that file. We find it in the source for CanvasItem:
Transform2D CanvasItem::get_global_transform() const {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!is_inside_tree(), get_transform());
#endif
if (global_invalid) {
const CanvasItem *pi = get_parent_item();
if (pi) {
global_transform = pi->get_global_transform() * get_transform();
} else {
global_transform = get_transform();
}
global_invalid = false;
}
return global_transform;
}
And there is the failed assert you saw: !is_inside_tree().
Oh, about that global_invalid. If you search for it on the source, you will find it is set to true when the node exits the scene tree or the transform is modified (that is in _notify_transform, which I'm not including here, but you can see it is called by _update_transform when it is in the scene tree).
What can we make out of all this?
The property global_position is simply syntactic sugar for the methods get_global_position and set_global_position.
The methods get_global_position and set_global_position work on the global transform, which is inherited from CanvasItem.
To figure out the global position, we need to work out the transforms on the parent nodes. Which means, we could also figure it out by going over the same process, and thus these methods technically not necessary.
It is done lazily. The global position gets invalidated, and it is recomputed on demand.
If the node is not in the scene tree… What parent nodes? In this case using global_position would be doing the same if we were using position. Thus, we could say global_position is not useful when the node is not in the scene tree.
There is an assert that tells you when you are using it and it is not in the scene tree. And that assert gave you the message you posted.

Scenekit - SCNLookupConstraint causes ConvertPosition of childNode not to update its position

I have a main node, with a sequence of childNodes. The childNodes are firePoints, so as the target comes into view, the main node rotates to target and the firepoint is an offset where I need to shoot from. It works fine if I target via some vector classes I built, but it is smoother if I use SCNConstraint on the main node. The main node (and firepoints) rotate to target, but the fire points vector values do not ever change when convertPosition is called. I can see that the fireNodes are rotating along with the base node properly. Thanks
func shoot()
{
isShooting = true
// Convert position so that projectile fires from FirePoint
let fireNode = gNodes.getNode(vName: attr.name + "FirePoint" + "\(attr.firePointsSequence)", vRequired: true, vError: "FP0-Sproj")
let fireNodeStart = gNodes.gameNodes.convertPosition(fireNode.presentation.position, from: attr.node)
print("FireNodePosition: \(fireNodeStart)
}
func setTarget()
{
attr.node.constraints = []
let vConstraint = SCNLookAtConstraint(target: targetNode)
vConstraint.isGimbalLockEnabled = true
attr.node.constraints = [vConstraint]
}
I had this problem. What solved it for me was using SCN's presentation scn. It has up to date positions.

What exactly does the outermost visit strategy do in Rascal?

I wrote the below Rascal code that is supposed to build a tree out of a map from node names to nodes, starting at the node mapped from "top". It should repeatedly replace the children of all nodes that have strings as children in result by the nodes nodeMap maps them to, until nothing changes anymore (fixpoint).
getNode returns the node a map[str,node] maps it to, or the key itself if it is not present as a key in the map. This works fine, as proves the fact that other code at the bottom of this question does work. However, the code directly below seems to run infintely even on very small inputs.
node nodeMapToNode(map[str, node] nodeMap) {
node result = nodeMap["top"];
return outermost visit(result) {
case node n: {
if ([*str children] := getChildren(n)) {
insert makeNode(getName(n), [getNode(child, nodeMap) | child <- children]);
}
}
}
}
The following code does work and returns in an instant on small inputs as I expected. This is, however, exactly what I understood outermost-visiting should do from what I understood from the Rascal tutor.
Can anyone explain to me what the difference between these code snippets is (besides the way they are written) and what I thus misunderstood about the effect of outermost visit? Also, I'd like to know if a shorter and/or nicer way to write this code - using something like outermost-visiting instead of writing the fixpoint by hand - does exist.
node nodeMapToNode(map[str, node] nodeMap) {
node result = nodeMap["top"];
node lastResult;
do {
lastResult = result;
result = visit(lastResult) {
case node n: {
if ([*str children] := getChildren(n)) {
insert makeNode(getName(n),
[getNode(child, nodeMap) | child <- children]);
}
}
}
} while (result != lastResult);
return result;
}
What is outermost?
The rascal tutor is very compact in it's explanation but let's start from there.
repeat a top-down traversal as long as the traversal changes the resulting value (compute a fixed-point).
which in rascal terms means that this:
r = outermost visit(x) {
case str s => s + "."
when size(s) < 3
};
is syntactic sugar for:
r = x;
solve(r) {
r = top-down visit(r) {
case str s => s + "."
when size(s) < 3
};
}
I think there are two common cases were outermost/innermost makes sense:
your replacement should be repeated multiple times on the same node
your replacement generate new nodes that match other patterns
Your specific example
Regarding the example in your question. The other manually rewritten outermost is actually an innermost. The default visit strategy is bottom-up.
In general, an bottom-up visit of the tree is a quicker than a top-down. Especially when you are rewriting it, since Rascal is immutable, building a new tree bottom-up is quicker.
So, perhaps replace your code with an innermost visit instead of an outermost?

Merge two binary trees in C

I am trying to merge two binary trees and this is what I have come up with. I believe that my Insertmerge functions are correct as well as my and merge functions work...I am having trouble with my merge helper. I need to do is post order, left then right, but I am not sure if what I am doing makes sense. Any thoughts?
SparseNode * mergehelper(SparseNode* C_array_1, SparseNode * array_2)
{
if(array_2 ==NULL)
{
return(C_array_1);
}
C_array_1= SparseArray_InsertMerge(C_array_1, ((array_2->left)->index),((array_2->left)-
>value));
C_array_1= SparseArray_InsertMerge(C_array_1, ((array_2->right)->index),((array_2->right)->value));
}
SparseNode* SparseArray_InsertMerge(SparseNode * array, int index, int value)
{
check(array);
if(array ==NULL)
{
return SparseNode_create(index, value);
}
if((array->index)==index)
{
if(array->value == -value)
array= SparseArray_remove (array, array->index);
else
array->value += value;
check(array);
return array;
}
if((array->index)>index)
{
array->left = SparseArray_insert(array->left, index, value);
}
if((array->index)<index)
{
array->right = SparseArray_insert(array->right, index, value);
}
check(array);
return array;
}
/*
post order traversal
new insert for merge
adapt for nonoverwriting
do a tree traversal
then add the node into the copied list in the work of the tree traversal
in merge. copy array1. call mergehelper with copy.
in helper. if null return, then insert left and insert right. print function is similiar
*/
SparseNode * SparseArray_merge(SparseNode * array_1, SparseNode * array_2)
{
SparseNode*Arr1_copy = copy(array_1);
Arr1_copy = mergehelper(Arr1_copy, array_2);
return (Arr1_copy);
}
The easiest way to merge two binary trees is to simply use the functionality that the trees already provide:
a traversal, extracting the values.
a means of inserting values.
So the solution then becomes (pseudo-code):
def mergeTrees (tree1, tree2):
valuePtr = tree2.getFirst();
while valuePtr != NULL:
tree1.addNode (valuePtr->value)
valuePtr = tree2.getNextAfter (valuePtr)
Following that, tree1 will then be a merge of the two trees and you can do what you want with tree2.
Now that's the basic idea - you may want to handle things like duplicate removal or error conditions, but it should be a good enough start to get you going. There's often little point in using the internals of a data structure when the published interface gives you a cleaner way.

Resources