-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
77a66aa
commit a6b8e89
Showing
10 changed files
with
489 additions
and
201 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = "" |
Oops, something went wrong.