Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generating RoadPoints from existing Curve3D? #206

Open
so1975 opened this issue Dec 13, 2024 · 3 comments
Open

Generating RoadPoints from existing Curve3D? #206

so1975 opened this issue Dec 13, 2024 · 3 comments
Labels
question Further information is requested

Comments

@so1975
Copy link

so1975 commented Dec 13, 2024

edit 2 : I dont know what happened, maybe the update from .51->.52 version changed it, but now the imported curve can be edited directly! (godot4.3). After importing scene should be saved, closed and opened again. The code below made is not a rock solid good solution, but might helps in some cases. The remaining question ; how to export just curves from the road-generator? Not just separate rp-parts?

Is it possible to generate proper RoadGenerator road from existing Curve3D?

Another question - how to get full continous curve from road-generator created road?

I can get roadpoint-curves, but those are alla separate curves and I dont know how to create proper curve from those. Only useful I can get is the transforms.origin point and create curve from those, but losss of curve information is enormous. Those transformations are a bit too complicated me to

Code (Godot 4.3, add it tool-script, give valid curve and chunk size to parse):
edit 3 - below (mostly) working code to make road from plain curve

@tool
extends Node3D

@export var road_manager : RoadManager
@export var generate_points := false :
	set(generate_points):
		generate_rp_points()
@export_category("settings")
@export var close_curve : bool = false
@export var use_start : bool = false
@export var start_m : int
@export var use_stop : bool = false
@export var stop_m : int
@export var chunk_length : int = 20
@export var density : int = 20
@export var n_mag : int = 8
@export var p_mag : int = 8
@export_file("*.tres") var curve_path : String
var rpg_process : bool = false;

func generate_rp_points():
	if rpg_process: return
	rpg_process = true
	if!curve_path: print("no curve_path!"); return
	# extrack points from track
	var curve : Curve3D = load(curve_path)
	
	rps_from_curve(curve,chunk_length,n_mag,p_mag,density)
	rpg_process = false
	
func rps_from_curve(
	curve:Curve3D,	
	chunk_length : int, 
	next_mag : int = 8,
	prev_mag : int= 8,
	interval : int = 20,
	container: RoadContainer = null,
	)->void:
	if!container:
		container = get_node_or_null(rps_container(road_manager))
	var transforms 	:=[]
	curve.bake_interval = interval
	var baked_tilts	:	Array = curve.get_baked_tilts()
	var amount	:	int	= baked_tilts.size()-1
	var baked_length : int = curve.get_baked_length()
	var tilt_step : float = float(baked_length) / float(amount)

	var start_from := 0
	var stop_to : int = baked_length
	if use_start: 
		start_from = start_m;
	if use_stop:
		stop_to = stop_m
		if stop_m > baked_length or stop_m < start_from: 
			stop_to = baked_length;
	
	for sample in range(start_from,stop_to,chunk_length):
		var trans = curve.sample_baked_with_rotation(sample)
		var tilt_index : int = clamp(int(sample / tilt_step), 0, amount)
		if tilt_index <= amount:
			trans = trans.rotated_local(
				Vector3(0,0,-1),(baked_tilts[tilt_index])
			)
		transforms.append(trans)
		
	for i in range(transforms.size()):
		var p = RoadPoint.new()
		p.transform = transforms[i]
		p.next_mag = next_mag
		p.prior_mag = prev_mag
		container.add_child(p)
		p.owner = get_tree().edited_scene_root
	var points_size = transforms.size()-1
	for i in range(transforms.size()):
		var next : int = i+1; 
		if next >= points_size: 
			next = 0
		var prev : int = i-1; 
		if prev < 0:
			prev = points_size
		var p : RoadPoint = container.get_child(i)

		var next_point_node = container.get_child(next)
		#var prev_point = container.get_child(prev)
		var this_direction = RoadPoint.PointInit.NEXT
		var target_direction = RoadPoint.PointInit.PRIOR
		if!close_curve and next == 0:
			container.get_children()[container.get_child_count()-1].queue_free()

			break
			#points_size:
			#p.next_pt_init = container.get_child(i-1).get_path()

		#p.connect_roadpoint(this_direction, next_point, target_direction)
		p.connect_roadpoint(target_direction, next_point_node, this_direction)
		
func rps_container(rm : RoadManager = null)->NodePath:
	if!rm:	rm = get_node_or_null(rps_manager())
	var node : RoadContainer = RoadContainer.new()
	rm.add_child(node)
	node.owner = get_tree().edited_scene_root
	return node.get_path()
	
func rps_manager(target_node : Node = null)->NodePath:
	if!target_node: target_node = self
	var node : RoadManager = RoadManager.new()
	target_node.add_child(node)
	node.owner = get_tree().edited_scene_root
	return node.get_path()
@TheDuckCow
Copy link
Owner

Hey there! I'd be happy to help. But to start with making sure I understand the goal here, the questions:

how to export just curves from the road-generator? Not just separate rp-parts?

Do you mean, creating one overall curve that represents the entire road network? There's no single solution that could really work for this, as native curves are (by definition) continuous without breaks and non branching. In contrast, the Road Generator lets you connect and disconnect road segments, join them up to prefab pieces (e.g. intersections) and so forth.

You could, however, write a function that tries to generates a curve for one continuous stretch of road at a time, ie a non broken sequence of RoadPoints. My main question is: what is the use case for this? What are you trying to do with this single continuous stretch of road? Is this to add details along the side of the road or something like this? And, is this for a procedural in-game generation use case, or more about e.g. placing curves to decorate items along the road in the Godot editor? I'm asking these questions so I can help better advise.

Is it possible to generate proper RoadGenerator road from existing Curve3D?

So you mean going from a hand-authored curve and then automatically creating roads from that? Again, I'd be curious to hear about the workflow you are trying to have where you'd start with such curves. Are they coming from another software, or is there a reason it's not convenient enough to just draw the road curves directly with the Road Generator menu?

But nonetheless, while we don't have built in functions this, your code snippet is somewhat similar to what we are doing in Wheel Steal Game. We add a series of RoadPoints with some distance between each one, placement and orientation is sampled along the curve to create more organic feeling procedurally generated roads.

Another question - how to get full continous curve from road-generator created road?

I think this is the same as the first question right? Let me know if not.

Hope this helps!

@TheDuckCow TheDuckCow added the question Further information is requested label Dec 13, 2024
@so1975
Copy link
Author

so1975 commented Dec 13, 2024

I use generated content and use curve / curves as a baseline. Currently npc-vehicles use center curve to take direction on the road. Other approach might be more scalable and easier to use if intersections or other roads are added - maybe some day. Principle is pretty much the same - to take direction to some point and go towards. I use rigid bodies with physics, and precise point is not needed - just a guideline. Terrain data also uses road-curve. I generate road, terrain and assets before game starts, so there is currently no generating after game starts.

I continued code a bit and now it take edge_(RFC) points from given container point-ranges. I just really don get, how I should rotate / transform taken path and its curvepoints to generate proper curve.
main func to do it looks now this ( the affine inverse-thing really makes a mess, its last thing I have tried );

func points_from_curve(curve : Curve3D, trans : Transform3D,rot:Vector3)->Array:
	curve.bake_interval = 10;
	var samples : int= 5;
	var curve_length : float= curve.get_baked_length()
	var chunk_length = float(float(curve_length) / float(samples))
	var points_set :=[]
	for sample in range(0,curve_length,chunk_length):
		var point: Transform3D = curve.sample_baked_with_rotation(sample)#.rotated()
		point = point.affine_inverse() * trans
		#point.rotated_local(Vector3(1,0,0),rot.x)
		#point.rotated_local(Vector3(0,1,0),rot.y)
		#point.rotated_local(Vector3(0,0,1),rot.z)
		points_set.append(point.origin + trans.origin)
	return points_set

@so1975
Copy link
Author

so1975 commented Dec 14, 2024

tool script to make curve from rp:s - far from perfect. Requires that edge curves are parsed by rg.

var making_curve_process : bool = false;

func make_curve_from_rp_points():
	if making_curve_process: return
	making_curve_process = true
	if!road_manager:	
		making_curve_process = false;	
		print("No road manager defined!"); return
	var process_nodes : Array = duplicate_paths()
	var paths : Array= process_nodes[0]
	var temp_node_path : NodePath = process_nodes[1]
	paths.reverse()
	process_duplicated_paths(paths)
	var temp_node = get_node_or_null(temp_node_path)
	if temp_node: 
		temp_node.queue_free()
	making_curve_process = false;	
	
func process_duplicated_paths(paths : Array):
	var center_curve := Curve3D.new()
	var pn = get_node(pathnode_container(path_node))
	for x in range(paths.size()):
		var path = get_node_or_null(paths[x]);
		if!path		:	continue
		var _curve = path.curve
		if!_curve	:	continue
		var points = points_from_curve(_curve,path)
		for pos in points:
			center_curve.add_point(pos)
	pn.curve = center_curve
	
func points_from_curve(curve : Curve3D, _path: Path3D)->Array:
	curve.bake_interval = 10;
	var samples : int= 5;
	var curve_length : float= curve.get_baked_length()
	var chunk_length = float(float(curve_length) / float(samples))
	var points_set :=[]
	for sample in range(0,curve_length,chunk_length):
		var point = curve.sample_baked(sample)
		var pos  = _path.to_global(point)
		points_set.append(pos)
	return points_set
	
func duplicate_paths()->Array:
	var points := []
	var road_containers :=[]
	var node_paths :=[]
	var temp_node := Node3D.new()
	add_child(temp_node)
	temp_node.owner = get_tree().edited_scene_root
	for ch in road_manager.get_containers():
		road_containers.append(ch)
		for obj in ch.get_roadpoints(true):
			var pt:RoadPoint = obj
			points.append(pt)
	for rp : RoadPoint in points:
		var p00 : Vector3 = rp.transform.origin
		var curve_to_get = "edge_C"
		var rp_path : Path3D
		for node in rp.get_children():
			if node.name == curve_to_get:
				rp_path = node.duplicate(true); break
		if!rp_path: continue
		temp_node.add_child(rp_path)
		rp_path.owner = get_tree().edited_scene_root
		rp_path.transform = rp.transform 
		node_paths.append(rp_path.get_path())
	return [node_paths,temp_node.get_path()]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants