Skip to content

Commit 0b9ab62

Browse files
authored
Merge pull request #170 from tom-leys/feature/scene_create_example
Add Scene Creation example
2 parents 784fc7b + 46e6be7 commit 0b9ab62

File tree

14 files changed

+370
-2
lines changed

14 files changed

+370
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ members = [
1111
"bindings_generator",
1212
"examples/hello_world",
1313
"examples/spinning_cube",
14+
"examples/scene_create",
1415
]

examples/hello_world/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ struct HelloWorld;
77

88
#[gdnative::methods]
99
impl HelloWorld {
10-
1110
fn _init(_owner: gdnative::Node) -> Self {
1211
HelloWorld
1312
}

examples/scene_create/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "scene_create"
3+
version = "0.1.0"
4+
authors = ["tom.leys <[email protected]>", "karroffel <[email protected]>"]
5+
6+
[lib]
7+
crate-type = ["cdylib"]
8+
9+
[dependencies]
10+
gdnative = { path = "../../gdnative" }
11+
euclid = "0.19.6"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[gd_scene load_steps=2 format=2]
2+
3+
[sub_resource type="CubeMesh" id=1]
4+
5+
[node name="Child" type="Spatial"]
6+
7+
[node name="CSGMesh" type="CSGMesh" parent="."]
8+
transform = Transform( 0.9, 0, 0, 0, 0.9, 0, 0, 0, 0.9, 0, 0, 0 )
9+
mesh = SubResource( 1 )
10+
11+
[node name="CSGMesh2" type="CSGMesh" parent="."]
12+
transform = Transform( 0.1, 0, 0, 0, 0.4, 0, 0, 0, 0.1, 0, 2.15568, 0 )
13+
mesh = SubResource( 1 )

examples/scene_create/Main.tscn

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[gd_scene load_steps=3 format=2]
2+
3+
[ext_resource path="res://Parent.gdns" type="Script" id=1]
4+
[ext_resource path="res://Panel.gd" type="Script" id=2]
5+
6+
[node name="Main" type="Spatial"]
7+
8+
[node name="Camera" type="Camera" parent="."]
9+
transform = Transform( 0.707107, 0.5, -0.5, 0, 0.707107, 0.707107, 0.707107, -0.5, 0.5, -9.65923, 12.7955, 9.30623 )
10+
current = true
11+
12+
[node name="Parent" type="Spatial" parent="."]
13+
script = ExtResource( 1 )
14+
15+
[node name="Ui" type="Node2D" parent="."]
16+
17+
[node name="Canvas" type="CanvasLayer" parent="Ui"]
18+
19+
[node name="Panel" type="Panel" parent="Ui/Canvas"]
20+
margin_top = 533.0
21+
margin_right = 1023.0
22+
margin_bottom = 598.0
23+
grow_horizontal = 2
24+
grow_vertical = 2
25+
script = ExtResource( 2 )
26+
27+
[node name="Add" type="Button" parent="Ui/Canvas/Panel"]
28+
margin_left = 28.8019
29+
margin_top = 18.0811
30+
margin_right = 160.802
31+
margin_bottom = 48.0811
32+
text = "Add Child"
33+
34+
[node name="Remove" type="Button" parent="Ui/Canvas/Panel"]
35+
margin_left = 179.803
36+
margin_top = 19.4414
37+
margin_right = 311.803
38+
margin_bottom = 49.4414
39+
text = "Remove Child"
40+
41+
[node name="Label" type="Label" parent="Ui/Canvas/Panel"]
42+
margin_left = 345.0
43+
margin_top = 21.0
44+
margin_right = 580.0
45+
margin_bottom = 40.0
46+
text = "No Children have been created yet"
47+
[connection signal="pressed" from="Ui/Canvas/Panel/Add" to="Ui/Canvas/Panel" method="_on_Add_pressed"]
48+
[connection signal="pressed" from="Ui/Canvas/Panel/Remove" to="Ui/Canvas/Panel" method="_on_Remove_pressed"]

examples/scene_create/Panel.gd

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
extends Panel
2+
3+
func _ready():
4+
pass # Replace with function body.
5+
6+
# There is a signal set up on the Add button which calls
7+
# this method. We pass the call into Parent.spawn_one
8+
# (This calls Rust)
9+
func _on_Add_pressed():
10+
var spawner_node = get_node("/root/Main/Parent")
11+
spawner_node.spawn_one("example string")
12+
13+
# There is a signal set up on the Remove button which calls
14+
# this method. We pass the call into Parent.remove_one
15+
# (This calls Rust)
16+
func _on_Remove_pressed():
17+
var spawner_node = get_node("/root/Main/Parent")
18+
spawner_node.remove_one("Another example string")
19+
20+
# This function is called from Rust. All we need there is this
21+
# node and the name "set_num_children"
22+
func set_num_children(children_count):
23+
# This is called from rust
24+
if children_count == 0 :
25+
$Label.text = "What did you go and kill all the children for!?"
26+
elif children_count == 1:
27+
$Label.text = "We have one child"
28+
else :
29+
$Label.text = "%d children have been created so far" % children_count
30+
31+
32+

examples/scene_create/Parent.gdns

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[gd_resource type="NativeScript" load_steps=2 format=2]
2+
3+
[ext_resource path="res://scene_create_library.gdnlib" type="GDNativeLibrary" id=1]
4+
5+
[resource]
6+
resource_name = "ChildSpawner"
7+
class_name = "SceneCreate"
8+
library = ExtResource( 1 )
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[gd_resource type="Environment" load_steps=2 format=2]
2+
3+
[sub_resource type="ProceduralSky" id=1]
4+
sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 )
5+
sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 )
6+
sky_curve = 0.25
7+
ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 )
8+
ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 )
9+
ground_curve = 0.01
10+
sun_energy = 16.0
11+
12+
[resource]
13+
background_mode = 2
14+
background_sky = SubResource( 1 )

examples/scene_create/icon.png

3.42 KB
Loading

examples/scene_create/icon.png.import

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[remap]
2+
3+
importer="texture"
4+
type="StreamTexture"
5+
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
6+
metadata={
7+
"vram_texture": false
8+
}
9+
10+
[deps]
11+
12+
source_file="res://icon.png"
13+
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
14+
15+
[params]
16+
17+
compress/mode=0
18+
compress/lossy_quality=0.7
19+
compress/hdr_mode=0
20+
compress/bptc_ldr=0
21+
compress/normal_map=0
22+
flags/repeat=0
23+
flags/filter=true
24+
flags/mipmaps=false
25+
flags/anisotropic=false
26+
flags/srgb=2
27+
process/fix_alpha_border=true
28+
process/premult_alpha=false
29+
process/HDR_as_SRGB=false
30+
process/invert_color=false
31+
stream=false
32+
size_limit=0
33+
detect_3d=true
34+
svg/scale=1.0

examples/scene_create/project.godot

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
; Engine configuration file.
2+
; It's best edited using the editor UI and not directly,
3+
; since the parameters that go here are not all obvious.
4+
;
5+
; Format:
6+
; [section] ; section goes between []
7+
; param=value ; assign values to parameters
8+
9+
config_version=4
10+
11+
_global_script_classes=[ ]
12+
_global_script_class_icons={
13+
14+
}
15+
16+
[application]
17+
18+
config/name="Godot Rust - Scene Create Example"
19+
run/main_scene="res://Main.tscn"
20+
config/icon="res://icon.png"
21+
22+
[rendering]
23+
24+
environment/default_environment="res://default_env.tres"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[general]
2+
3+
singleton=false
4+
load_once=false
5+
symbol_prefix="godot_"
6+
reloadable=true
7+
8+
[entry]
9+
10+
OSX.64="../../target/debug/libscene_create.dylib"
11+
X11.64="../../target/debug/libscene_create.so"
12+
Windows.64="res://../../target/debug/scene_create.dll"
13+
14+
[dependencies]
15+
16+
OSX.64=[ ]
17+
UWP.64=[ ]
18+
X11.64=[ ]

examples/scene_create/src/lib.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#[macro_use]
2+
extern crate gdnative;
3+
extern crate euclid;
4+
5+
use euclid::vec3;
6+
use gdnative::{GodotString, PackedScene, ResourceLoader, Spatial, Variant};
7+
8+
#[derive(Debug, Clone, PartialEq)]
9+
pub enum ManageErrs {
10+
CouldNotMakeInstance,
11+
RootClassNotSpatial(String),
12+
}
13+
14+
#[derive(gdnative::NativeClass)]
15+
#[inherit(gdnative::Spatial)]
16+
struct SceneCreate {
17+
// Store the loaded scene for a very slight performance boost but mostly to show you how.
18+
template: Option<PackedScene>,
19+
children_spawned: u32,
20+
}
21+
22+
// Demonstrates Scene creation, calling to/from gdscript
23+
//
24+
// 1. Child scene is created when spawn_one is called
25+
// 2. Child scenes are deleted when remove_one is called
26+
// 3. Find and call functions in a node (Panel)
27+
// 4. Call functions in GDNative (from panel into spawn/remove)
28+
//
29+
// Note, the same mechanism which is used to call from panel into spawn_one and remove_one can be
30+
// used to call other GDNative classes here in rust.
31+
32+
#[gdnative::methods]
33+
impl SceneCreate {
34+
fn _init(_owner: gdnative::Spatial) -> Self {
35+
SceneCreate {
36+
template: None, // Have not loaded this template yet.
37+
children_spawned: 0,
38+
}
39+
}
40+
41+
#[export]
42+
fn _ready(&mut self, _owner: gdnative::Spatial) {
43+
self.template = load_scene("res://Child_scene.tscn");
44+
match &self.template {
45+
Some(_scene) => godot_print!("Loaded child scene successfully!"),
46+
None => godot_print!("Could not load child scene. Check name."),
47+
}
48+
}
49+
50+
#[export]
51+
unsafe fn spawn_one(&mut self, mut owner: gdnative::Spatial, message: GodotString) {
52+
godot_print!("Called spawn_one({})", message.to_string());
53+
54+
let template = if let Some(template) = &self.template {
55+
template
56+
} else {
57+
godot_print!("Cannot spawn a child because we couldn't load the template scene");
58+
return;
59+
};
60+
61+
// Create the scene here. Note that we are hardcoding that the parent must at least be a
62+
// child of Spatial in the template argument here...
63+
match instance_scene::<Spatial>(template) {
64+
Ok(mut spatial) => {
65+
// Here is how you rename the child...
66+
let key_str = format!("child_{}", self.children_spawned);
67+
spatial.set_name(GodotString::from_str(&key_str));
68+
69+
let x = (self.children_spawned % 10) as f32;
70+
let z = (self.children_spawned / 10) as f32;
71+
spatial.translate(vec3(-10.0 + x * 2.0, 0.0, -10.0 + z * 2.0));
72+
73+
// You need to parent the new scene under some node if you want it in the scene.
74+
// We parent it under ourselves.
75+
owner.add_child(Some(spatial.to_object()), false);
76+
self.children_spawned += 1;
77+
}
78+
Err(err) => godot_print!("Could not instance Child : {:?}", err),
79+
}
80+
81+
let num_children = owner.get_child_count();
82+
update_panel(&mut owner, num_children);
83+
}
84+
85+
#[export]
86+
unsafe fn remove_one(&mut self, mut owner: gdnative::Spatial, str: GodotString) {
87+
godot_print!("Called remove_one({})", str.to_string());
88+
let num_children = owner.get_child_count();
89+
if num_children <= 0 {
90+
godot_print!("No children to delete");
91+
return;
92+
}
93+
94+
assert_eq!(self.children_spawned as i64, num_children);
95+
96+
let last_child = owner.get_child(num_children - 1);
97+
if let Some(mut node) = last_child {
98+
node.queue_free();
99+
self.children_spawned -= 1;
100+
}
101+
102+
update_panel(&mut owner, num_children - 1);
103+
}
104+
}
105+
106+
fn init(handle: gdnative::init::InitHandle) {
107+
handle.add_class::<SceneCreate>();
108+
}
109+
110+
pub fn load_scene(path: &str) -> Option<PackedScene> {
111+
let scene = ResourceLoader::godot_singleton().load(
112+
GodotString::from_str(path), // could also use path.into() here
113+
GodotString::from_str("PackedScene"),
114+
false,
115+
);
116+
117+
scene.and_then(|s| s.cast::<PackedScene>())
118+
}
119+
120+
/// Root here is needs to be the same type (or a parent type) of the node that you put in the child
121+
/// scene as the root. For instance Spatial is used for this example.
122+
unsafe fn instance_scene<Root>(scene: &PackedScene) -> Result<Root, ManageErrs>
123+
where
124+
Root: gdnative::GodotObject,
125+
{
126+
let inst_option = scene.instance(0); // 0 - GEN_EDIT_STATE_DISABLED
127+
128+
if let Some(instance) = inst_option {
129+
if let Some(instance_root) = instance.cast::<Root>() {
130+
Ok(instance_root)
131+
} else {
132+
Err(ManageErrs::RootClassNotSpatial(
133+
instance.get_name().to_string(),
134+
))
135+
}
136+
} else {
137+
Err(ManageErrs::CouldNotMakeInstance)
138+
}
139+
}
140+
141+
unsafe fn update_panel(owner: &mut gdnative::Spatial, num_children: i64) {
142+
// Here is how we call into the panel. First we get its node (we might have saved it
143+
// from earlier)
144+
let panel_node_opt = owner
145+
.get_parent()
146+
.and_then(|parent| parent.find_node(GodotString::from_str("Panel"), true, false));
147+
if let Some(panel_node) = panel_node_opt {
148+
// Put the Node
149+
let mut as_variant = Variant::from_object(&panel_node);
150+
match as_variant.call(
151+
&GodotString::from_str("set_num_children"),
152+
&[Variant::from_u64(num_children as u64)],
153+
) {
154+
Ok(_) => godot_print!("Called Panel OK."),
155+
Err(_) => godot_print!("Error calling Panel"),
156+
}
157+
} else {
158+
godot_print!("Could not find panel node");
159+
}
160+
}
161+
162+
godot_gdnative_init!();
163+
godot_nativescript_init!(init);
164+
godot_gdnative_terminate!();

0 commit comments

Comments
 (0)