Skip to content

Commit

Permalink
More FuncGodot examples
Browse files Browse the repository at this point in the history
  • Loading branch information
RhapsodyInGeek committed Jul 29, 2024
1 parent 77a66aa commit a6b8e89
Show file tree
Hide file tree
Showing 10 changed files with 489 additions and 201 deletions.
32 changes: 32 additions & 0 deletions FuncGodot Entities/GD Extension/prop_dynamic.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[gd_resource type="Resource" script_class="FuncGodotFGDPointClass" load_steps=3 format=3 uid="uid://rorln1pcp1bq"]

[ext_resource type="Resource" uid="uid://dnjlim0ik6hty" path="res://tb/fgd/base/prop_base.tres" id="1_lvmib"]
[ext_resource type="Script" path="res://addons/func_godot/src/fgd/func_godot_fgd_point_class.gd" id="3_xvxts"]

[resource]
script = ExtResource("3_xvxts")
apply_rotation_on_map_build = true
classname = "prop_dynamic"
description = "Dynamic prop entity."
func_godot_internal = false
base_classes = Array[Resource]([ExtResource("1_lvmib")])
class_properties = {
"anim": "",
"editor_display_path": "",
"next_anim": "",
"prop_path": "",
"scale": Vector3(1, 1, 1)
}
class_property_descriptions = {
"anim": "Starting animation override.",
"editor_display_path": "Display path for editor model. Not used in Godot.",
"next_anim": "Animation to be played when this entity is triggered.",
"prop_path": "Local Resource or User path to GLTF or GLB file in Godot."
}
meta_properties = {
"color": Color(0.8, 0, 0.8, 1),
"model": "editor_display_path",
"size": AABB(-8, -8, 0, 8, 8, 16)
}
node_class = "PropDynamic"
name_property = ""
39 changes: 39 additions & 0 deletions FuncGodot Entities/GD Extension/prop_multi_mesh.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[gd_resource type="Resource" script_class="FuncGodotFGDPointClass" load_steps=4 format=3 uid="uid://dg5i0lvmh0dye"]

[ext_resource type="Resource" uid="uid://3nrgyhofbhnq" path="res://tb/fgd/base/globalname_base.tres" id="1_5yybl"]
[ext_resource type="Script" path="res://addons/func_godot/src/fgd/func_godot_fgd_point_class.gd" id="1_vr34g"]
[ext_resource type="Resource" uid="uid://dqeio0pq2js4m" path="res://tb/fgd/base/shadowcast_base.tres" id="2_hbd8m"]

[resource]
script = ExtResource("1_vr34g")
apply_rotation_on_map_build = true
classname = "prop_multi_mesh"
description = "Multi mesh prop that generates mesh instances across target geometry. Surface targets can be picked by either proximity or matching the target property with an entity's globalname property."
func_godot_internal = false
base_classes = Array[Resource]([ExtResource("1_5yybl"), ExtResource("2_hbd8m")])
class_properties = {
"amount": 128,
"base_scale": 1.0,
"mesh_res_path": "",
"random_rotation": 0.0,
"random_scale": 0.0,
"random_tilt": 0.0,
"target": "",
"target_mesh": ""
}
class_property_descriptions = {
"amount": "Number of mesh instances to generate.",
"base_scale": "Base scale factor for mesh instances.",
"mesh_res_path": "Resource filepath of a mesh to instance across the target geometry. Overridden by target_mesh, if target_mesh is valid.",
"random_rotation": "Range of rotation for mesh instances on the Y axis.",
"random_scale": "Scale factor offset range for mesh instances.",
"random_tilt": "Range of tilt for mesh instances on the X and Z axes.",
"target": "Globalname of the target geometry object to place the multi mesh instances across.",
"target_mesh": "Globalname of the geometry to copy across target. This can be used to instance brush entities. This overrides mesh_res_path, if target_mesh is valid."
}
meta_properties = {
"color": Color(0.8, 0, 0.8, 1),
"size": AABB(-16, -16, -16, 16, 16, 16)
}
node_class = "PropMultiMesh"
name_property = ""
44 changes: 44 additions & 0 deletions FuncGodot Entities/GD Script/mesh_face.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class_name MeshFace
extends RefCounted

enum Side {
SIDE_OVER,
SIDE_UNDER,
SIDE_SPAWNING,
SIDE_COPLANAR
}

var vertex: Array[Vector3] = [Vector3.ZERO,Vector3.ZERO,Vector3.ZERO]

enum ClockDirection { CLOCKWISE, COUNTER_CLOCKWISE}

func get_plane() -> Plane:
return Plane(vertex[0], vertex[1], vertex[2])

func get_random_point_inside() -> Vector3:
var a: float = randf_range(0, 1)
var b: float = randf_range(0, 1)
if a > b:
var c: float = a
a = b
b = c
return vertex[0] * a + vertex[1] * (b - a) + vertex[2] * (1 - b)

func get_area() -> float:
return (vertex[0] - vertex[1]).cross(vertex[0] - vertex[2]).length() * 0.5

func print_face_info(face_index: int = -1) -> void:
var s: String = "Face"
if face_index > -1:
s += " " + str(face_index)
s += ":\n"
for v in vertex:
s += str(v) + "\n"
s += str(get_area())
s += "\n"
print(s)

func _init(v1: Vector3, v2: Vector3, v3: Vector3) -> void:
vertex[0] = v1
vertex[1] = v2
vertex[2] = v3
172 changes: 172 additions & 0 deletions FuncGodot Entities/GD Script/prop_multi_mesh.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
@tool
class_name PropMultiMesh
extends MultiMeshInstance3D

@export var populate_multi_mesh: bool :
set(val):
_populate_multi_mesh()

@export var target: String = ""

@export var targetname: String = ""

@export var target_mesh: String = ""

@export var mesh_res_path: String = ""

@export var amount: int = 128 :
set(val):
amount = max(val, 0)

@export var random_rotation: float = 0.0 :
set(val):
random_rotation = abs(val)

@export var random_tilt: float = 0.0 :
set(val):
random_tilt = abs(val)

@export var base_scale: float = 1.0 :
set(val):
base_scale = max(abs(val), 0.01)

@export var random_scale: float = 0.0 :
set(val):
random_scale = abs(val)

func _populate_multi_mesh() -> void:
# Find surface target
var mesh_targ: Array[MeshInstance3D] = [null, null]

for i in 2:
var targ_path: NodePath
if i == 0:
targ_path = "entity_" + target
else:
targ_path = "entity_" + target_mesh

if get_parent().has_node(targ_path):
var temp: Node = get_parent().get_node(targ_path)
if temp is MeshInstance3D:
mesh_targ[i] = temp as MeshInstance3D
elif temp.get_child_count() > 0:
for child in temp.get_children():
if child is MeshInstance3D:
mesh_targ[i] = child as MeshInstance3D
break

# No valid surface target! Find one within range
if !mesh_targ[0] or !mesh_targ[0].mesh:
var world_ents: Array[Node] = get_tree().get_nodes_in_group("WORLD")
for ent in world_ents:
for child in ent.get_children():
if child is MeshInstance3D and child.mesh:
if child.global_position.distance_to(global_position) < 8.0:
mesh_targ[0] = child
break
if mesh_targ[0]:
break
if !mesh_targ[0]:
printerr("No target found for PropMultiMesh " + name)
return

# Cache the mesh
var mesh: Mesh = null
if mesh_targ[1] != null:
mesh = mesh_targ[1].mesh
if !mesh and FileAccess.file_exists(mesh_res_path):
mesh = load(mesh_res_path)
if !mesh:
printerr("No valid mesh set for PropMultiMesh " + name)
return

# Prepare target surface
var geo_xform: Transform3D = global_transform.affine_inverse() * mesh_targ[0].global_transform
var verts: PackedVector3Array = mesh_targ[0].mesh.get_faces()
if verts.size() == 0 or verts.size() % 3 != 0:
printerr("PropMultiMesh " + name + " target has invalid mesh")
return

var faces: Array[MeshFace] = []
faces.resize(verts.size() / 3)
var facecount: int = faces.size()
for i in facecount:
faces[i] = MeshFace.new(verts[i*3], verts[i*3+1], verts[i*3+2])
for j in 3:
faces[i].vertex[j] = geo_xform * faces[i].vertex[j]

var area_accum: float = 0.0
var triangle_area_map: Dictionary = {}
for i in facecount:
var area: float = faces[i].get_area()
if area < 0.00001:
continue
triangle_area_map[area_accum] = i
area_accum += area

if triangle_area_map.is_empty() or area_accum == 0:
printerr("PropMultiMesh " + name + " couldn't map area")
return

# Generate the MultiMesh resource
multimesh = MultiMesh.new()
multimesh.transform_format = MultiMesh.TRANSFORM_3D
multimesh.instance_count = amount
multimesh.mesh = mesh

# Place instances
for i in amount:
var area_pos: float = randf_range(0, area_accum)
var area_key: float = 0
for key in triangle_area_map.keys():
if abs(key - area_pos) < abs(area_key - area_pos):
area_key = key
var index: int = wrapi(triangle_area_map[area_key], 0, facecount)

var face: MeshFace = faces[index]

var pos: Vector3 = face.get_random_point_inside()
var normal: Vector3 = face.get_plane().normal
var op_axis: Vector3 = (face.vertex[0] - face.vertex[1]).normalized()

var xform: Transform3D
xform.origin = pos
xform = xform.looking_at(pos + op_axis, normal)

var rot: float = deg_to_rad(random_rotation)
var tilt: float = deg_to_rad(random_tilt)

var post_xform: Basis
post_xform = post_xform.rotated(post_xform.y, randf_range(-rot, rot))
post_xform = post_xform.rotated(post_xform.z, randf_range(-tilt, tilt))
post_xform = post_xform.rotated(post_xform.x, randf_range(-tilt, tilt))
xform.basis = post_xform * xform.basis
xform.basis *= max(abs(base_scale + randf_range(-random_scale, random_scale)), 0.01)

multimesh.set_instance_transform(i, xform)

func _func_godot_apply_properties(props: Dictionary) -> void:
if props.has("target"):
target = props["target"] as String
if props.has("target_mesh"):
target_mesh = props["target_mesh"] as String
if props.has("targetname"):
targetname = props["targetname"] as String
if props.has("mesh_res_path"):
mesh_res_path = props["mesh_res_path"] as String
if props.has("amount"):
amount = max((props["amount"] as int), 0)
if props.has("random_rotation"):
random_rotation = abs(props["random_rotation"] as float)
if props.has("random_tilt"):
random_tilt = props["random_tilt"] as float
if props.has("base_scale"):
base_scale = maxf(abs(props["base_scale"] as float), 0.01)
if props.has("random_scale"):
random_scale = abs(props["random_scale"] as float)

_populate_multi_mesh()

func _init() -> void:
add_to_group("prop_multi_mesh")

39 changes: 39 additions & 0 deletions FuncGodot Entities/GD Script/prop_multi_mesh.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[gd_resource type="Resource" script_class="FuncGodotFGDPointClass" load_steps=3 format=3 uid="uid://dmmnqnundewsi"]

[ext_resource type="Script" path="res://addons/func_godot/src/fgd/func_godot_fgd_point_class.gd" id="1_vr34g"]
[ext_resource type="Script" path="res://entities/props/prop_multi_mesh.gd" id="2_no2cd"]

[resource]
script = ExtResource("1_vr34g")
script_class = ExtResource("2_no2cd")
apply_rotation_on_map_build = true
classname = "prop_multi_mesh"
description = "Multi mesh prop that generates mesh instances across surface target geometry. Surface targets can be picked by either proximity or matching the target property with an entity's globalname property."
func_godot_internal = false
base_classes = Array[Resource]([])
class_properties = {
"amount": 128,
"base_scale": 1.0,
"mesh_res_path": "",
"random_rotation": 0.0,
"random_scale": 0.0,
"random_tilt": 0.0,
"target": "",
"target_mesh": ""
}
class_property_descriptions = {
"amount": "Number of mesh instances to generate.",
"base_scale": "Base scale factor for mesh instances.",
"mesh_res_path": "Resource filepath of a mesh to instance across the target geometry. Overridden by target_mesh, if target_mesh is valid.",
"random_rotation": "Range of rotation for mesh instances on the Y axis.",
"random_scale": "Scale factor offset range for mesh instances.",
"random_tilt": "Range of tilt for mesh instances on the X and Z axes.",
"target": "Globalname of the target geometry object to place the multi mesh instances across.",
"target_mesh": "Globalname of the geometry to copy across target. This can be used to instance brush entities. This overrides mesh_res_path, if target_mesh is valid."
}
meta_properties = {
"color": Color(0.8, 0, 0.8, 1),
"size": AABB(-16, -16, -16, 16, 16, 16)
}
node_class = "MultiMeshInstance3D"
name_property = ""
Loading

0 comments on commit a6b8e89

Please sign in to comment.