Godot gdnative Rust access property defined in the editor - rust

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>>.

Related

Get children from ListBox (gtk4)

When I was using gtk3 I was able to access a ListBox's children this way:
let list_box = ListBox::new();
let label = Label::new(Some("Label 1"));
list_box.append(&label);
for row in list_box.children() {
list_box.remove(&row);
}
In gtk4, it seems like children() doesn't exist anymore: "no method named `children` found for struct `gtk4::ListBox`".
I checked ListBox in the gtk4 docs, but I couldn't find anything related to accessing a ListBox's children.
How to access a ListBox's children in gtk4?
There is observe_children, but like it's name suggests you should not modify the container while using it. Or more specifically you can't keep using it's iterator once you modify the underlying container.
For your usecase you can iterate and remove children using first_child/last_child like this:
while let Some(row) = list_box.last_child() {
list_box.remove(&row);
}

Why would you use the spread operator to spread a variable onto itself?

In the Google Getting started with Node.js tutorial they perform the following operation
data = {...data};
in the code for sending data to Firestore.
You can see it on their Github, line 63.
As far as I can tell this doesn't do anything.
Is there a good reason for doing this?
Is it potentially future proofing, so that if you added your own data you'd be less likely to do something like data = {data, moreData}?
#Manu's answer details what the line of code is doing, but not why it's there.
I don't know exactly why the Google code example uses this approach, but I would guess at the following reason (and would do the same myself in this situation):
Because objects in JavaScript are passed by reference, it becomes necessary to rebuild the 'data' object from it's constituent parts to avoid the original data object being further modified by the ref.set(data) call on line 64 of the example code:
await ref.set(data);
For example, in MongoDB, when you pass an object into a write or update method, Mongo will actually modify the object to add extra properties such as the datetime it was insert into a collection or it's ID within the collection. I don't know for sure if Firestore does the same, but if it doesn't now, it's possible that it may in future. If it does, and if your original code that calls the update method from Google's example code goes on to further manipulate the data object that it originally passed, that object would now have extra properties on it that may cause unexpected problems. Therefore, it's prudent to rebuild the data object from the original object's properties to avoid contamination of the original object elsewhere in code.
I hope that makes sense - the more I think about it, the more I'm convinced that this must be the reason and it's actually a great learning point.
I include the full original function from Google's code here in case others come across this in future, since the code is subject to change (copied from https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/bookshelf/books/firestore.js at the time of writing this answer):
// Creates a new book or updates an existing book with new data.
async function update(id, data) {
let ref;
if (id === null) {
ref = db.collection(collection).doc();
} else {
ref = db.collection(collection).doc(id);
}
data.id = ref.id;
data = {...data};
await ref.set(data);
return data;
}
It's making a shallow copy of data; let's say you have a third-party function that mutates the input:
const foo = input => {
input['changed'] = true;
}
And you need to call it, but don't want to get your object modified, so instead of:
data = {life: 42}
foo(data)
// > data
// { life: 42, changed: true }
You may use the Spread Syntax:
data = {life: 42}
foo({...data})
// > data
// { life: 42 }
Not sure if this is the particular case with Firestone but the thing is: spreading an object you get a shallow copy of that obj.
===
Related: Object copy using Spread operator actually shallow or deep?

How to Use RustEmbed to load Images into an fltk app?

I'm trying to use the RustEmbed crate to embed some svg icons into my executable. I don't see any documentation on how to turn the data returned from Asset::get("icons/some.svg") into an actual image. My GUI library is fltk, and I want to create an fltk::image::SvgImage using Asset but SvgImage::new() loads an svg from a path, not raw byte data. Because it loads the svg from a path and not from raw byte data, does that mean I can't use RustEmbed for embedding the icons into my target build?
I want to do this because I feel like embedding my image assets into the executable will help avoid IO errors when the path of my executable is changed during deployment/install/build. I thought this one of the intentions of the RustEmbed crate.
use rust_embed::RustEmbed;
use fltk::image::*;
#[derive(RustEmbed)]
#[folder = "examples/public/"]
struct Asset;
fn main() {
let svg = Asset::get("icons/eye.svg").unwrap();
println!("{:?}", std::str::from_utf8(svg.as_ref()));
//just prints some long array of numbers [60, 115,118,...60,47,115]
wind_svg = SvgImage::load(svg).unwrap();
}
gives error:
the trait `AsRef<std::path::Path>` is not implemented for `std::option::Option<Cow<'_, [u8]>>`
Turns out that fltk::image::SvgImage has a from_data() function. This can be used create load the svg from byte data:
use rust_embed::RustEmbed;
use fltk::image::*;
#[derive(RustEmbed)]
#[folder = "examples/assets/"]
struct Asset;
fn main() {
let bytes = Asset::get("icons/eye.svg").unwrap();
let svg = SvgImage::from_data(std::str::from_utf8(&bytes).unwrap()).unwrap();
}
For more useful information on dependencies check out this reddit thread.

How can I retrieve the geometry coordinates of a NpgsqlTypes.PostgisGeometry type field from the NpgsqlDataReader?

.NET 4.5, C#, Npgsql 3.1.0
I have a query which retrieves a Postgis geometry field - the only way I could see of doing this was:
public class pgRasterChart
{
...
public NpgsqlTypes.PostgisGeometry GEOMETRY;
...
}
...
NpgsqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
pgRasterChart chart = new pgRasterChart();
chart.GEOMETRY = (PostgisGeometry) reader.GetValue(21);
...
This functions but I need to get at the coordinates of the GEOMETRY field and I can't find a way of doing that? I want to use the coordinates to display the results on an OpenLayers map.
Any answers most gratefully received. This is my first post so my apologies if the etiquette is clumsy or question unclear.
Providing another answer because the the link above to the documentation for PostGisTypes is now broken.
PostGisGeometry is an abstract base class that does not contain anything more exiting than the SRID. Instead, you want to cast the object obtained by your datareader to the appropriate type (any of the following):
PostGisLineString
PostGisMultiLineString
PostGisMultiPoint
PostGisMultiPolygon
PostGisPoint
PostGisPolygon
These classes have ways of getting to the coordinates.
eg:
...
NpgsqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
var geom = (PostgisLineString) reader.GetValue(0);
var firstCoordinate = geom[0]; // Coordinate in linestring at index 0
var X = firstCoordinate.X;
var Y = firstCoordinate.Y;
...
As you can see here
https://github.com/npgsql/npgsql/blob/dev/src/Npgsql.LegacyPostgis/PostgisTypes.cs
PostgisGeometry types are a set of xy pairs.
For example, a linestring is an array of points, a polygon is an array of rings and so on..
You could traverse those structures and get the coordinates.
However, if you just want to display geometries using openlayers, I suggest you to use the wkt format.
You should change your query, selecting st_astext(geometry) instead of geometry, than treat the result as a string and give it back to OpenLayers.
Then use OpenLayers.Geometry.fromWKT to parse the WKT into an OpenLayers.Geometry

Array access not allowed on OpenFL movieclips

UDATED
How do I go about this?
I got this from Main.hx:
function onMouseOver(e:MouseEvent){
if(Std.is(e.currentTarget, MovieClip)){
initializer (cast e.currentTarget,["scaleX",1.5,"scaleY",1.5])
}
}
Then this is the pointed function in my Animation Class
//here if i set mc:Dynamic everything goes great! but when this one
function initializer(mc:MovieClip, vars:Array<Dynamic>){
var varsLength:Int = Math.round(vars.length/2);
for(m in 0...varsLength){
ini[m] = mc[vars[2*m]];
}
}
then when i compile it, an error appears:
Error: Array access is not allowed in flash.display.MovieClip
How do I resolve this?
EDIT:
vars: are properties of the MovieClip, for example when I pass these parameters:
initializer (mcClip1,["scaleX",1.5,"scaleY",1.5])
so:
vars = ["scaleX",1.5,"scaleY",1.5]
and:
ini[m] will store "scaleX" and "scaleY"`
X-Ref: https://groups.google.com/forum/#!topic/haxelang/_hkyt__Rrzw
In AS3, you can access fields of an object via their String name using [] (array access). This is called Reflection.
In Haxe, Reflection works differently - you need to make use of the Reflect API.
It's considered bad practice - it's not type-safe, which means the compiler can do very little to help you with error messages, and it's quite slow as well. This is why the usage makes it very explicit that Reflection is actually going on (while in AS3, this fact is somewhat hidden). Consider if there are other ways of solving this problem that don't require Reflection.
Now, to get back to your example, here's what it would look like in Haxe:
function onMouseOver(e:MouseEvent){
if (Std.is(e.currentTarget, MovieClip)) {
initializer(cast e.currentTarget, ["scaleX", 1.5, "scaleY", 1.5])
}
}
function initializer(mc:MovieClip, vars:Array<Dynamic>) {
for (m in 0...Std.int(vars.length / 2)) {
ini[m] = Reflect.getProperty(mc, vars[2*m]);
}
}
Btw, your loop was running for too long since you only use half of the values in the array - if you don't divide it by two like I did, you'll end up with [scaleX, scaleY, null, null] instead of the desired [scaleX, scaleY].

Resources