Skip to content

Commit

Permalink
HexMapCellIdRef: passing a cell id to GDScript
Browse files Browse the repository at this point in the history
Had to add a class for wrapping HexMapCellId to pass to GDScript.

There's a whole series of problems here.  First of all,
ClassDB::bind_method() can only bind methods that return a type that can
be cast to a Variant.  From some resources the only two viable options
were Ref<T> or a pointer to an Object subclass[^1].  Pointer means we
need to somehow manage memory that we've entirely passed up to GDScript.
Instead we use Ref<T>.

Second problem, you cannot use Object subclasses on the stack in
godot-cpp[^2].  This means if we make HexMapCellId a subclass of
godot::Object, we cannot use it on the stack, or easily construct it
using an initializer.  Insteat we'd have to use `memnew()` all over the
place.

So we're using a wrapper class `HexMapCellId < public godot::RefCounted`.
It's a boiler-plate heavy approach, but it works for now.

Did propose to godot-cpp to add a compiler flag to disable the
guard-rail that keeps us from using `godot::Object` subclasses on the
stack[^3].

[^1]: https://forum.godotengine.org/t/returning-custom-classes-from-c-gdextension/65920/18
[^2]: godotengine/godot-cpp#1446 (comment)
[^3]: godotengine/godot-cpp#1446 (comment)
  • Loading branch information
dmlary committed Sep 15, 2024
1 parent a9c6ddc commit 1ee88cd
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 106 deletions.
21 changes: 19 additions & 2 deletions demo/node_3d.tscn
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
[gd_scene load_steps=2 format=4 uid="uid://bt0soj2wvavie"]
[gd_scene load_steps=4 format=4 uid="uid://bt0soj2wvavie"]

[ext_resource type="MeshLibrary" uid="uid://ygm46u2uobsa" path="res://tileset.tres" id="1_43pbi"]
[ext_resource type="PackedScene" uid="uid://wcaukixhlbo2" path="res://RayPickerCamera/ray_picker_camera.tscn" id="1_wtynj"]

[sub_resource type="CapsuleMesh" id="CapsuleMesh_bq4b2"]

[node name="Node3D" type="Node3D"]

[node name="RayPickerCamera" parent="." node_paths=PackedStringArray("hex_map", "marker") instance=ExtResource("1_wtynj")]
transform = Transform3D(1, 0, 0, 0, 0.5, 0.866025, 0, -0.866025, 0.5, 0, 26.0113, 10.4268)
fov = 35.0
hex_map = NodePath("../HexMap")
label_cells = false
marker = NodePath("../GridMap/Marker")

[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 66.1477, 0)
shadow_enabled = true

[node name="GridMap" type="GridMap" parent="."]
mesh_library = ExtResource("1_43pbi")

[node name="Marker" type="MeshInstance3D" parent="GridMap"]
mesh = SubResource("CapsuleMesh_bq4b2")

[node name="HexMap" type="HexMap" parent="."]
mesh_library = ExtResource("1_43pbi")
cell_size = Vector3(1.154, 1, 1.154)
cell_center_y = false
data = {
"cells": PackedByteArray("")
"cells": PackedByteArray("/P8AAP7/AAAAAP3/AAD9/wAAAAD+/wAA/P8AAAAA//8AAPz/AAAAAP7/AAD9/wAAAAD9/wAA/v8AAAAA+/8AAP//AAAAAPz/AAD//wAAAAD//wAA/f8AAAAA/v8AAP7/AAAAAPv/AAAAAAAAAAD8/wAAAAAAAAAA/f8AAP//AAAAAP7/AAD//wAAAAD//wAA/v8AAAAAAAAAAP3/AAAAAAEAAAD8/wAAAAABAAAA/f8AAAAA/f8AAAAAAAAAAP//AAD//wAAAAAAAAAA/v8AAAAAAAAAAP//AAAAAP7/AAAAAAAAAAD8/wAAAQAAAAAA/f8AAAEAAAAAAP7/AAABAAAAAAD//wAAAAAAAAAA+/8AAAEAAAAAAPv/AAD+/wAAAAD6/wAAAQAAAAAA+v8AAAAAAAAAAPv/AAD9/wAAAAD8/wAA/f8AAAAA/f8AAPz/AAAAAPr/AAD//wAAAAD8/wAA/P8AAAAA/f8AAPv/AAAAAP7/AAD7/wAAAAD//wAA+/8AAAAAAAAAAPv/AAAAAAEAAAD7/wAAAAAAAAAA/P8AAAAAAgAAAPv/AAAAAAIAAAD6/wAAAAABAAAA+v8AAAAAAAAAAPr/AAAAAP//AAD6/wAAAAD+/wAA+v8AAAAA/f8AAPr/AAAAAPz/AAD7/wAAAAD7/wAA/P8AAAAA+v8AAP7/AAAAAPn/AAAAAAAAAAD5/wAAAQAAAAAA+f8AAP7/AAAAAPr/AAD9/wAAAAD5/wAA//8AAAAAAgAAAPz/AAAAAAIAAAD9/wAAAAABAAAA/v8AAAAAAgAAAP7/AAAAAAEAAAD//wAAAAAAAAAAAAAAAAAA//8AAAEAAAAAAAYAAAD6/wAAAAAFAAAA+/8AAAAABQAAAPz/AAAAAAUAAAD9/wAAAAAFAAAA/v8AAAAABgAAAP3/AAAAAAcAAAD9/wAAAAAHAAAA/P8AAAAACAAAAPz/AAAAAAgAAAD7/wAAAAAIAAAA+v8AAAAABwAAAPr/AAAAAAYAAAD8/wAAAAAHAAAA+/8AAAAABgAAAPv/AAAAAA==")
}
metadata/_editor_floors_ = [0, 0, 0, 0]
24 changes: 24 additions & 0 deletions demo/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,29 @@ config_version=5
[application]

config/name="HexGrid Demo"
run/main_scene="res://node_3d.tscn"
config/features=PackedStringArray("4.3", "Forward Plus")
config/icon="res://icon.svg"

[display]

window/stretch/mode="viewport"
window/dpi/allow_hidpi=false

[input]

zoom_in={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":8,"position":Vector2(425, 26),"global_position":Vector2(444, 117),"factor":0.0300018,"button_index":4,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
zoom_out={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":16,"position":Vector2(604, 30),"global_position":Vector2(623, 121),"factor":0.0300018,"button_index":5,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
place_marker={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(177, 34),"global_position":Vector2(196, 125),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
210 changes: 123 additions & 87 deletions demo/tileset.tres

Large diffs are not rendered by default.

146 changes: 145 additions & 1 deletion demo/tileset.tscn

Large diffs are not rendered by default.

21 changes: 17 additions & 4 deletions src/hex_map/hex_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@
/**************************************************************************/

#include "hex_map.h"
#include "godot_cpp/variant/basis.hpp"
#include "godot_cpp/variant/utility_functions.hpp"
#include "hex_map_cell_id.h"
#include "tile_orientation.h"

#include "godot_cpp/variant/basis.hpp"
#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/main_loop.hpp>
#include <godot_cpp/classes/material.hpp>
Expand Down Expand Up @@ -114,8 +113,6 @@ bool HexMap::_set(const StringName &p_name, const Variant &p_value) {
cell.cell = cells.decode_u32(offset);
offset += 4;

UtilityFunctions::print("loaded one cell");

cell_map[key] = cell;
}
}
Expand Down Expand Up @@ -571,6 +568,19 @@ static inline Vector3i oddr_to_axial(Vector3i oddr) {
return Vector3i(q, oddr.y, oddr.z);
}

HexMapCellId HexMap::local_to_cell_id(const Vector3 &local_position) const {
Vector3 unit_pos = local_position / cell_size;
return HexMapCellId::from_unit_point(unit_pos);
}

Ref<HexMapCellIdRef> HexMap::_local_to_cell_id(
const Vector3 &p_local_position) const {
Ref<HexMapCellIdRef> ref;
ref.instantiate();
ref->set(local_to_cell_id(p_local_position));
return ref;
}

HexMap::CellId HexMap::local_to_map(const Vector3 &p_local_position) const {
// convert x/z point into axial hex coordinates
// https://www.redblobgames.com/grids/hexagons/#pixel-to-hex
Expand Down Expand Up @@ -1458,6 +1468,9 @@ void HexMap::_bind_methods() {
D_METHOD("local_region_to_map", "local_point_a", "local_point_b"),
&HexMap::_local_region_to_map);

ClassDB::bind_method(D_METHOD("local_to_cell_id", "local_position"),
&HexMap::_local_to_cell_id);

#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("resource_changed", "resource"),
&HexMap::resource_changed);
Expand Down
13 changes: 6 additions & 7 deletions src/hex_map/hex_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ using namespace godot;
#define RS RenderingServer

class HexMapCellId;
class HexMapCellIdRef;

class HexMap : public Node3D {
GDCLASS(HexMap, Node3D);
Expand Down Expand Up @@ -195,13 +196,6 @@ class HexMap : public Node3D {

void _recreate_octant_data();

struct BakeLight {
RS::LightType type = RS::LightType::LIGHT_DIRECTIONAL;
Vector3 pos;
Vector3 dir;
float param[RS::LIGHT_PARAM_MAX] = {};
};

_FORCE_INLINE_ Vector3 _octant_get_offset(const OctantKey &p_key) const {
return Vector3(p_key.x, p_key.y, p_key.z) * cell_size * octant_size;
}
Expand Down Expand Up @@ -313,6 +307,11 @@ class HexMap : public Node3D {
Basis get_basis_with_orthogonal_index(int p_index) const;
TypedArray<Vector3i> get_cell_neighbors(const Vector3i p_cell) const;

HexMapCellId local_to_cell_id(const Vector3 &local_position) const;
Ref<HexMapCellIdRef> _local_to_cell_id(
const Vector3 &local_position) const;
Vector3 cell_id_to_local(const HexMapCellId &cell_id) const;

CellId local_to_map(const Vector3 &p_local_position) const;
Vector3 map_to_local(const Vector3i &p_map_position) const;
// Vector3 map_to_local(const HexMapCellId &p_cell_id) const;
Expand Down
3 changes: 3 additions & 0 deletions src/hex_map/hex_map_cell_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ HexMapCellId::operator String() const {
// clang-format off
return (String)"{ .q = " + itos(q) +
", .r = " + itos(r) +
", .s = " + itos(s()) +
", .y = " + itos(y) + "}";
// clang-format on
}
Expand All @@ -75,3 +76,5 @@ std::ostream &operator<<(std::ostream &os, const HexMapCellId &value) {
os << " }";
return os;
}

String HexMapCellIdRef::_to_string() { return (String)this->cell_id; }
19 changes: 19 additions & 0 deletions src/hex_map/hex_map_cell_id.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "godot_cpp/classes/ref_counted.hpp"
#include "godot_cpp/classes/wrapped.hpp"
#include "godot_cpp/variant/vector3.hpp"
#include "hex_map.h"

Expand Down Expand Up @@ -58,3 +60,20 @@ class HexMapCellId {

// added for testing
std::ostream &operator<<(std::ostream &os, const HexMapCellId &value);

// wrapper to return a HexMapCellId to GDscript
class HexMapCellIdRef : public RefCounted {
GDCLASS(HexMapCellIdRef, RefCounted)

HexMapCellId cell_id;

public:
const HexMapCellId &operator*() const { return cell_id; }
HexMapCellId &operator*() { return cell_id; }
void set(const HexMapCellId &other) { cell_id = other; };

String _to_string();

protected:
static void _bind_methods() {};
};
10 changes: 5 additions & 5 deletions src/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@

#include "hex_map/editor/hex_map_editor_plugin.h"
#include "hex_map/hex_map.h"
#include "hex_map/hex_map_cell_id.h"
#include "test_node.h"
#include "test_node_editor_plugin.h"

using namespace godot;

void initialize_hexmap_module(ModuleInitializationLevel p_level) {
if (p_level == godot::MODULE_INITIALIZATION_LEVEL_SCENE) {
ClassDB::register_class<HexMapCellIdRef>();
ClassDB::register_class<HexMap>();
ClassDB::register_class<TestNode>();
}
Expand All @@ -66,14 +68,12 @@ void uninitialize_hexmap_module(ModuleInitializationLevel p_level) {

extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT
hexmap_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address,
GDExtensionBool GDE_EXPORT hexmap_library_init(
GDExtensionInterfaceGetProcAddress p_get_proc_address,
const GDExtensionClassLibraryPtr p_library,
GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(
p_get_proc_address,
p_library,
r_initialization);
p_get_proc_address, p_library, r_initialization);

init_obj.register_initializer(initialize_hexmap_module);
init_obj.register_terminator(uninitialize_hexmap_module);
Expand Down

0 comments on commit 1ee88cd

Please sign in to comment.