Node's world position is always are origin - ARKit anchor - position

I am just trying to find world position and rotation of nodes after I detect and add planes to my AR scene.
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
if !(anchor is ARPlaneAnchor) {
return
}
let plane = //to visualise planes
node.addChildNode(plane)
print("Node : \(node.worldPosition)"
}
but node.worldPosition always returns a SCNVector3(x: 0.0, y: 0.0, z: 0.0). Looks like the local position. Only the anchor gives its respective world position. I want to be able to transform the node according to the world transformations.
Thanks in advance.

tldr: Print node.worldPosition inside renderer(_:didUpdate:for:)
You are using renderer(_:didAdd:for:). Think of this as where you initially respond to a fresh new node on a new anchor. The new node hasn't yet been updated with relevant information. Adding a child plane to this new node as you did would be a good response.
In contrast, renderer(_:didUpdate:for:) is used to respond to actual updates on the node. This is when we get to see the previously added node behave as we expect. That's why you are seeing unexpected values.

Related

Godot gdnative Rust access property defined in the editor

In order to learn about the engine I'm trying my hand at a very simple project - proceduraly generating a sphere using the editor and the gdnative Rust bindings.
I'm trying to follow up this tutorial which uses GDScript and convert the
code to Rust.
I'm having trouble figuring out how to access a property defined in the editor.
I've been reading the docs and searching around the web for a week now but there is
something that escapes me and I'm not able to arrive at an understanding of how to proceed about it.
What I want to do, is access the mesh property, of type ArrayMesh, much like in the tutorial that I linked above, and attach to it the arrays that I generated for the vertices - basically bind those arrays to the ArrayMesh. Here is my scene:
[gd_scene load_steps=4 format=2]
[ext_resource path="res://procedural_earth.gdnlib" type="GDNativeLibrary" id=1]
[sub_resource type="ArrayMesh" id=4]
[sub_resource type="NativeScript" id=3]
resource_name = "ProcEarth"
class_name = "ProcEarth"
library = ExtResource( 1 )
[node name="Earth" type="Spatial"]
[node name="Sphere" type="MeshInstance" parent="."]
mesh = SubResource( 4 )
script = SubResource( 3 )
[node name="Camera" type="Camera" parent="."]
transform = Transform( 0.572229, -0.327396, 0.751909, 0, 0.916856, 0.399217, -0.820094, -0.228443, 0.524651, 4.71648, 2.5, 3.45846 )
current = true
The ArrayMesh structure that I'm interested in, is called mesh in the above scene and is part of the node named "Sphere"(mentioning just for the sake of clarity).
I have the following Rust code:
#[derive(NativeClass)]
#[inherit(MeshInstance)]
#[register_with(register_properties)]
struct ProcEarth {
// ...
}
impl ProcEarth {
// ...
#[export]
fn _ready(&mut self, owner: &MeshInstance) {
let mut arr = VariantArray::new_shared();
// ...
let blend_shapes = VariantArray::new_shared();
owner.add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, blend_shapes, 0);
}
}
But that does not work as the error I get is:
no method named `add_surface_from_arrays` found for reference `&gdnative::gdnative_bindings::MeshInstance` in the current scope
method not found in `&gdnative::gdnative_bindings::MeshInstance`rustc(E0599)
Does anyone know how could I access in the Rust code that property from the editor, in order to properly set my ArrayMesh? Is there any tutorial, article, video that exemplifies that?
Any pointers highly appreciated as I'm currently stuck into this technicality
and cannot progress my learning.
I'm using Godot version v3.4.stable.official with gdnative 0.9.3 on Linux.
The method add_surface_from_arrays is defined in ArrayMesh. Given the error you got, you are trying to call it on a MeshInstance.
We can confirm that with the source code, since you get owner: &MeshInstance and you are calling owner.add_surface_from_arrays(…).
Usually you would create an ArrayMesh and call add_surface_from_arrays on it passing an array with the vertex data. Afterwards you should be able to call set_mesh on the MeshInstance passing the ArrayMesh.
let mut am = ArrayMesh::new();
let mut arr = VariantArray::new_shared();
// …
let blend_shapes = VariantArray::new_shared();
am.add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, blend_shapes, 0);
owner.set_mesh(am.cast::<Mesh>());
I believe you can call mesh on the MeshInstance to retrieve it. Be aware that it can a Mesh (ArrayMesh or PrimitiveMesh) or nil. The method mesh is documented to return Option<Ref<Mesh, Shared>>.

Godot: add_child() on an instanced node's children to recursively create objects

I'm creating a little program that creates image patterns like these using line segments that rotate around each other:
image from Engare Steam store page for reference
How do I tell Godot to create instances of the Polygon2D scene I'm using as line segments with origins on Position2D nodes that exist as children in the Polygon2D scene? Here is a sample of my code:
const SHAPE_MASTER = preload("res://SpinningBar.tscn")
...
func bar_Maker(bar_num, parent_node):
for i in range(bar_num):
var GrabbedInstance = SHAPE_MASTER.instance()
parent_node.add_child(GrabbedInstance)
bar_Maker(bar_num - 1, $Polygon2D/RotPoint)
...
func _physics_process(delta):
...
if Input.is_action_just_pressed("Switch Item"): # from an older version of the program, just bound to ctrl
bar_Maker(segment_count, BarParent)
bar_num is the number of bars to instance, set elsewhere (range 1-6).
parent_node in the main scene is just a Node2D called BarParent.
The SpinningBar.tscn I'm instancing as GrabbedInstance has a Position2D node called "RotPoint" at the opposite end of the segment from the object's origin. This is the point I would like successive segments to rotate around (and also put particle emitters here to trace lines, but that is a trivial issue once this first one is answered).
Running as-is and creating more than 1 line segment returns "Attempt to call function 'add_child' in base 'null instance' on a null instance." Clearly I am adding the second (and later) segments incorrectly, so I know it's related to how I'm performing recursion/selecting new parents for segments 1+ node deep.
Bang your head against the wall and you will find the answer:
func bar_Maker(bar_num, parent_node):
for i in range(bar_num):
var GrabbedInstance = SHAPE_MASTER.instance()
parent_node.add_child(GrabbedInstance)
var new_children = GrabbedInstance.get_children()
bar_Maker(bar_num - 1, new_children[0])
If someone is aware of a more elegant way to do this please inform me and future readers. o7

Fabric.js - Programmatically transforming group objects relative to a central image object

Using Fabric.js, I have an image object that acts like a background where other group objects (consisting of rectangle and text) can be placed over it.
I would like to be able to programmatically rotate the image (90 degrees clockwise or counter-clockwise) when user clicks the corresponding button, and then rotate all other group objects relative to the image.
I was able to get this to work with the Fabric.js group feature, but the performance progressively degraded over a few rotations even with just one group object.
Looking for alternatives, I changed to use the approach described in the tranformation example here: http://fabricjs.com/using-transformations. However, the group object is not placed in the expected position after rotation.
Would anyone have any pointers on what the problem might be?
Here's the relevant code that handles just one group object for simplicity:
private rotateObjects(rotationAngle: number): void
{
let groups = this.Canvas.getObjects().filter(x => x !== this.ActiveImage);
let imageTransform = this.ActiveImage.calcTransformMatrix();
let invertedImageTransform = fabric.util.invertTransform(imageTransform);
let annotationRelationship = fabric.util.multiplyTransformMatrices(invertedImageTransform, (groups[0]).calcTransformMatrix());
this.ActiveImage.rotate(rotationAngle);
let newTransform = fabric.util.multiplyTransformMatrices(this.ActiveImage.calcTransformMatrix(), annotationRelationship);
let options = fabric.util.qrDecompose(newTransform);
groups[0].set({
flipX: false,
flipY: false,
});
groups[0].setPositionByOrigin(new fabric.Point(options.translateX, options.translateY),
"center",
"center"
);
groups[0].set(options);
groups[0].setCoords();
this.Canvas.renderAll();
}
Here are screenshots of before and after rotation. Notice that the yellow group rectangle is not placed exactly over the words "Manny Bello".
Before rotation: https://imgur.com/RcS4ekZ
After rotation: https://imgur.com/nk8gStf

Amcharts v4 custom SVG

Is there a way to dynamically build an SVG using sprites in amcharts 4?
Example: screenhot
There are 20 different types which are represented by colors.
Each pin can contain a multitude of types.
So an example can be that a pin has 3 types and will consist out of 3 colors.
I have an SVG path which is a circle.
With regular JS and SVG i can create a path for each type and change the stroke color, strokedasharray and strokedashoffset.
This results in the nice circle with 3 colors.
However this seems to be impossible to do with amcharts 4.
For starters, strokedashoffset is not even a supported property for a sprite. Why would you bother supporting strokedasharray and then ignore strokedashoffet?!
The second problem is finding out how to pass data to the sprite.
This is an example of a data object I pass to the mapImageSeries class.
[{
amount: 3,
client: undefined,
colorsArr: {0: "#FFB783", 1: "#FD9797", 2: "#77A538"},
dashArray: "500,1000",
dashOffset: 1500,
divided: 500,
global: true,
groupId: "minZoom-1",
hcenter: "middle",
id: "250",
latitude: 50.53398,
legendNr: 8,
longitude: 9.68581,
name: "Fulda",
offsetsArr: {0: 0, 1: 500, 2: 1000},
scale: 0.5,
title: "Fulda",
typeIds: (3) ["4", "18", "21"],
typeMarker: " type-21 type-18 type-4",
vcenter: "bottom",
zoomLevel: 5
}]
It seems impossible to pass the colors down to the sprite.
var svgPath = 'M291,530C159,530,52,423,52,291S159,52,291,52s239,107,239,239c0,131.5-106.3,238.3-237.7,239'
var mainPin1 = single.createChild(am4core.Sprite)
mainPin1.strokeWidth = 100
mainPin1.fill = am4core.color('#fff')
mainPin1.stroke = am4core.color('#ff0000')
mainPin1.propertyFields.strokeDasharray = 'dashArray'
mainPin1.propertyFields.strokeDashoffset = 'dashOffset'
mainPin1.path = svgPath
mainPin1.scale = 0.04
mainPin1.propertyFields.horizontalCenter = 'hcenter'
mainPin1.propertyFields.verticalCenter = 'vbottom'
With what you've provided, simulating your custom SVGs is beyond the scope of what can be answered, so I'll try tackling:
applying stroke-dashoffset despite lack of innate library support. (I see you've added a feature request on GitHub for it, so why the library doesn't include it, when/if it will, can be left for discussion there.)
passing data/colors to the Sprite
For both we're going to have to wait until the instances of Sprites are ready along with their data. Presuming your single variable is a reference to a MapImageSeries.mapImages.template, we can set up an "inited" event like so:
single.events.once("inited", function(event){
// ...
});
Our data and data placeholders don't really support nested arrays/objects in general, since your colors are nested within a field, we can find them via:
event.target.dataItem.dataContext.colorsArr
You can then set the fill and stroke on the Sprite or event.target.children.getIndex(0) manually from there (in my demo below, the index will be 1 because mainPin1 is not the first/only child created on the MapImage template).
As for stroke-dashoffset, you can access the actual rendered SVGElement via sprite.group.node and just use setAttribute.
I forked our map image demo from our map image data guide and added all the above to it here:
https://codepen.io/team/amcharts/pen/6a3d87ff3bdee7b85000fe775af9e583

Hololens Placing Objects With Spatial Understanding

I have been running SpatialUnderstandingExample scene from holo-toolkit. Couldnt figure out how to place my objects into the scene. I want to replace those small boxes that comes default with my own objects. How can I do that?
Thanks
edit: found the draw box but how do i push my object there?
edit2: finally pushed an object at the position but still code is very complicated its messing up with the size and shape of my object. Will try to make it clean and neat.
It's been a while since I've looked at that example so hopefully I remember its method name's correctly. It contains a "DrawBox" method that is called after a successful call to get a location from spatial understanding. The call that creates the box looks something like this:
DrawBox(toPlace, Color.red);
Replace this call with the following (assuming "toPlace" contains the results from the spatial understanding call and "model" contains the model you are trying to place there):
var rotation = Quaternion.LookRotation(toPlace.Normal, Vector3.up);
// Stay center in the square but move down to the ground
var position = toPlace.Postion - new Vector3(0, RequestedSize.y * .5f, 0);
// instantiate the hologram from a model
GameObject newObject = Instantiate(model, position, rotation) as GameObject;
if (newObject != null)
{
// Set the parent of the new object the GameObject it was placed on
newObject.transform.parent = gameObject.transform;
}

Resources