Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: 🛠️ Build HEGo
on:
push:
branches:
- main # or your preferred branch

jobs:
make-submodule:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Configure Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

- name: Create or update release branch
run: |
# Get commit info for descriptive message
COMMIT_SHA="${{ github.sha }}"
COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s" $COMMIT_SHA)
COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an" $COMMIT_SHA)
SHORT_SHA=$(echo $COMMIT_SHA | cut -c1-7)

# Create new release branch from main or update existing one
git checkout -B release

# Clear everything except .git
find . -maxdepth 1 ! -name '.git' ! -name '.' -exec rm -rf {} +

# Copy README.md to addon folder first, then copy addon contents to root
git checkout main -- README.md demo/addons/hego
cp README.md demo/addons/hego/

# Fix image paths in README.md for new structure
sed -i 's|demo/addons/hego/assets/|assets/|g' demo/addons/hego/README.md

cp -r demo/addons/hego/* .
rm -rf demo

# Add and commit changes with descriptive message
git add .
git commit -m "Release: Update addon for submodule use

Based on commit $SHORT_SHA by $COMMIT_AUTHOR:
$COMMIT_MESSAGE" || echo "No changes to commit"

# Push to release branch
git push origin release --force
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,21 @@
- 🎥 **[Introduction Video](https://youtu.be/cviGlmKmFQ8)** - Visual walkthrough

### Quick Install

**Option 1: Git Submodule (Recommended)**
```bash
# Add HEGo as a submodule in your Godot project
git submodule add -b release https://github.com/peterprickarz/hego.git addons/hego
git submodule update --init --recursive
```

**Option 2: Manual Download**
1. Download the latest release
2. Extract to your Godot project's `addons/` folder
3. Enable the plugin in Project Settings

> **Note**: The `release` branch is automatically updated with each commit to `main` and contains only the addon files, making it perfect for submodule use.

## Features

- **Scriptable API**: Built with GDScript integration in mind for maximum flexibility
Expand Down
88 changes: 74 additions & 14 deletions demo/addons/hego/HEGoNode3D.gd
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
@icon('res://addons/hego/assets/houdini.svg')
@tool
extends Node3D

class_name HEGoNode3D

# The asset definition name in Houdini, e.g. Sop/my_tool.hda
@export_tool_button('Select HDA', "FileDialog") var select_hda_btn = _show_select_hda_dialog
## The asset definition name in Houdini, e.g. Sop/my_tool.hda
@export var asset_name: String
# Parm stash stores the parameters as a byte blob which stores parms between sessions
## Parm stash stores the parameters as a byte blob which stores parms between sessions
@export var parm_stash: PackedByteArray
# Input stash to store references to the inputs between sessions
## Input stash to store references to the inputs between sessions
@export var input_stash: Array


Expand All @@ -17,6 +17,7 @@ var hego_asset_node: HEGoAssetNode
# Create var to store references to the input and merge nodes in the session
var hego_input_nodes: Dictionary


func cook():
# Ensure valid AssetNode object
if not hego_asset_node: hego_asset_node = HEGoAssetNode.new()
Expand Down Expand Up @@ -107,14 +108,14 @@ func cook():
handle_mesh_output()
handle_multimesh_output()
handle_object_spawn_output()



func handle_mesh_output():
# use config to fetch output mesh
var fetch_surfaces_default_config = load("res://addons/hego/surface_filters/fetch_surfaces_default.tres")
# retrieve dictionary output, containing the mesh in godots surface_array format
var dict = hego_asset_node.fetch_surfaces(fetch_surfaces_default_config)
for hego_mesh_instance_key in dict.keys():

var arr_mesh = ArrayMesh.new()
var surface_id = 0
for hego_material_key in dict[hego_mesh_instance_key]:
Expand Down Expand Up @@ -226,9 +227,8 @@ func handle_mesh_output():
mesh_instance.create_convex_collision()
elif hego_col_type == 3:
mesh_instance.create_trimesh_collision()



func handle_object_spawn_output():
print("[HEGoNode3D]: Handling Object Spawn Output")
var fetch_points_config = load("res://addons/hego/point_filters/fetch_points_default_object_spawning.tres")
Expand Down Expand Up @@ -371,6 +371,7 @@ func handle_object_spawn_output():
# Log for debugging
#print("[HEGoNode3D]: Spawned %s at %s under %s" % [new_node.name, p, parent_node.get_path()])


# Helper function to apply custom properties from a nested dictionary
func apply_custom_properties(obj: Object, properties: Dictionary):
for key in properties.keys():
Expand Down Expand Up @@ -410,6 +411,7 @@ func apply_custom_properties(obj: Object, properties: Dictionary):
else:
push_warning("[HEGoNode3D]: Invalid property format for %s, expected dictionary with hego_val" % key)


# Helper function to set a single property
func set_property(obj: Object, property: String, value):
var prop_info = obj.get_property_list().filter(func(p): return p.name == property)
Expand All @@ -426,6 +428,7 @@ func set_property(obj: Object, property: String, value):
else:
push_warning("[HEGoNode3D]: Property %s does not exist on %s, skipping" % [property, obj.get_class()])


# Helper function to check type compatibility
func is_compatible_type(value, expected_type: int, expected_class: String) -> bool:
var actual_type = typeof(value)
Expand All @@ -451,6 +454,7 @@ func is_compatible_type(value, expected_type: int, expected_class: String) -> bo

return false


func handle_multimesh_output():
print("[HEGoNode3D]: Handling Multimesh Output")
var fetch_points_config = load("res://addons/hego/point_filters/fetch_points_default_multimesh_instancing.tres")
Expand Down Expand Up @@ -614,6 +618,7 @@ func float_to_int_triplet_dict(float_array: Array, int_array: Array) -> Dictiona

return result


func array_to_index_dict(float_array: Array) -> Dictionary:
var result: Dictionary = {}

Expand All @@ -625,7 +630,8 @@ func array_to_index_dict(float_array: Array) -> Dictionary:
result[value] = [i]

return result



func update_hego_input_node(hego_input_node, input_node_path, settings):
var scene_root = get_tree().edited_scene_root
var input = scene_root.get_node_or_null(input_node_path)
Expand All @@ -642,7 +648,7 @@ func update_hego_input_node(hego_input_node, input_node_path, settings):
else:
print("[HEGoNode3D]: Input is neither Path3D nor MeshInstance3D")
return hego_input_node


func create_hego_input_node(input_node_path, settings):
var scene_root = get_tree().edited_scene_root
Expand All @@ -660,21 +666,27 @@ func create_hego_input_node(input_node_path, settings):
print("[HEGoNode3D]: Input is neither Path3D nor MeshInstance3D")
return input_node


func hego_use_bottom_panel():
return true


func hego_get_asset_node():
return hego_asset_node;


func hego_stash_parms(preset: PackedByteArray):
parm_stash = preset



func hego_get_parm_stash(preset: PackedByteArray):
return parm_stash



func hego_get_input_stash():
return input_stash



func hego_set_input_stash(input_array: Array):
# input_array is an array of inputs, as each Houdini input can combine
# multiple inputs, each input is an array itself, storing node names and ids
Expand All @@ -693,15 +705,18 @@ func hego_set_input_stash(input_array: Array):
result.append(input_dict)
input_stash = result


func hego_get_asset_name():
return asset_name



func repeat_indent(indent: int) -> String:
var result := ""
for i in range(indent):
result += " " # 4 spaces
return result


func pretty_print(value, indent := 0) -> String:
var indent_str = repeat_indent(indent)
var next_indent_str = repeat_indent(indent + 1)
Expand All @@ -727,3 +742,48 @@ func pretty_print(value, indent := 0) -> String:

else:
return str(value)


func _show_select_hda_dialog():
if Engine.is_editor_hint():
var viewport = EditorInterface.get_editor_viewport_3d()
var picker_scene = preload("res://addons/hego/ui/asset_picker_dialog.tscn")
var picker = picker_scene.instantiate()

# Add to the scene tree temporarily
viewport.add_child(picker)

# Connect the signal to handle the selection
picker.asset_selected.connect(_on_asset_selected)

# Show the dialog
picker._populate_tree()
picker.popup_centered()


func _on_asset_selected(selected_asset: String):
# Clear old HDA data when selecting a new asset
_clear_hda_data()

asset_name = selected_asset
notify_property_list_changed()
print("[HEGoNode3D]: Selected asset: ", selected_asset)

func _clear_hda_data():
# Reset the asset node's internal HAPI node ID to force re-instantiation
if hego_asset_node:
hego_asset_node.reset_node_id()

# Clear parameter stash so we start fresh
parm_stash = PackedByteArray()

# Clear input references
input_stash.clear()
hego_input_nodes.clear()

# Clear any existing output nodes
var outputs_node = get_node_or_null("Outputs")
if outputs_node:
outputs_node.queue_free()

print("[HEGoNode3D]: Cleared old HDA data and reset node ID")
Binary file modified demo/addons/hego/bin/hego.windows.template_debug.x86_64.dll
Binary file not shown.
Binary file modified demo/addons/hego/bin/~hego.windows.template_debug.x86_64.dll
Binary file not shown.
25 changes: 25 additions & 0 deletions demo/addons/hego/hego.gd
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,37 @@ func _enter_tree():
# Add HEGo project settings
_add_project_settings()


func _exit_tree():
# Clean-up of the plugin goes here.
print("[HEGo]: Plugin exiting, cleaning up Houdini session...")

# Stop any active Houdini session
_cleanup_houdini_session()

if editor_selection and editor_selection.selection_changed.is_connected(_on_selection_changed):
editor_selection.selection_changed.disconnect(_on_selection_changed)

remove_import_plugin(import_plugin)
import_plugin = null

# Remove bottom panel
if bottom_panel:
remove_control_from_bottom_panel(bottom_panel)
bottom_panel = null

print("[HEGo]: Plugin cleanup completed")


func _cleanup_houdini_session():
if HEGoAPI.get_singleton() and HEGoAPI.get_singleton().is_session_active():
print("[HEGo]: Stopping active Houdini session...")
var stop_success = HEGoAPI.get_singleton().stop_session()
if stop_success:
print("[HEGo]: Houdini session stopped successfully")
else:
print("[HEGo]: Warning: Failed to stop Houdini session")


func _on_selection_changed():
var selected_nodes = editor_selection.get_selected_nodes()
Expand All @@ -39,6 +63,7 @@ func _on_selection_changed():
if bottom_panel:
bottom_panel.update_hego_asset_node(selected_node)


func _add_project_settings():
# Add Houdini installation path setting if it doesn't exist
var setting_name = "hego/houdini_installation_path"
Expand Down
Loading