godot icon indicating copy to clipboard operation
godot copied to clipboard

Add `PhysicsServer2/3D::space_step()` to step physics simulation manually

Open Daylily-Zeleen opened this issue 2 years ago • 85 comments

Implement #1373 In #2821, it seems like the topic is quite big, but I think this pr can solve it.

Here is a simple demonstrate video of rollbackable/recordable physics simulation create by this pr:

https://user-images.githubusercontent.com/61624558/234483154-004bf831-9a7a-4e16-b1e8-a3f723fa49c9.mp4

But this pr is not perfect, there has two points which confuse me (I'm not familiar with multi-threads and GodotPhysics), and I mark them at below.

Here is rollback demo: physics_rollback_test.zip

Daylily-Zeleen avatar Apr 26 '23 05:04 Daylily-Zeleen

Hello @Daylily-Zeleen, for thread-safety, you might want to call the functions sync() and end_sync() of the PhysicsServer classes an example of usage is in the function Main::iteration() in file main/main.cpp

Saw your PR when making mine ; yours has more general usage but might need some tweaks.

Lcbx avatar May 14 '23 18:05 Lcbx

for thread-safety, you might want to call the functions sync() and end_sync() of the PhysicsServer classes an example of usage is in the function Main::iteration() in file main/main.cpp

In my opinion, the PhysicsSpace which be stepped by step() is handled by developers, to run in which thread, sync or not, are controled by developers themselves.

Instead of call sync() and end_sync() in step(), expose spcas_sync(RID p_space) and space_end_sync(RID p_space) may be better.

I need people, who familiar with godot physics and server design, to instruct me.

Daylily-Zeleen avatar May 15 '23 05:05 Daylily-Zeleen

When will this feature be merged? I need this functionality to assist me in implementing my network synchronization.

Az-qianchen avatar Sep 13 '23 07:09 Az-qianchen

@Az-qianchen it hasn't been reviewed yet, so there's no real timeframe for this being merged at the moment, the questions of the OP about some details would need to be resolved as well by someone experienced with the physics

If you can test it and give your results and comments it would help the process

AThousandShips avatar Sep 13 '23 07:09 AThousandShips

https://github.com/godotengine/godot/assets/52212525/ab00512a-ca3e-412f-9812-a8404d5e5e7a

I am not sure if the way I use is correct, but when I simulation is positive, I can get the correct result. When the value is negative, the result seems to have an error.

Az-qianchen avatar Sep 13 '23 10:09 Az-qianchen

When the value is negative, the result seems to have an error.

How to step a physics space is not this pr's work.

I guess your purpose of passing a negative time to space_step() is to roll the physics space back to the previous state (for example, 0.1 second before). This cannot be achieved through space_step(), this method is to simulate a physical space forward, you can passing a negative delta time to simulate, but it just simulate forward by a negative delta time.

The right way to implement rollback feature is store your physics objects' state, and just restore them when you want to roll back. Typically, you should create a independent physics space to realize your physics simulation instead of using default space, then use space_step() to setp this space, to make your physics simulation under your control fully.

Daylily-Zeleen avatar Sep 13 '23 12:09 Daylily-Zeleen

you should create an independent physics space to realize

I encountered some obstacles when creating a physical space. Do I need to copy all PhysicsBody3D objects to the new physical space, and all objects must be StaticBody3D to perform space_step() on a single object and obtain the correct collision? Is there other shortcuts?

Az-qianchen avatar Sep 14 '23 07:09 Az-qianchen

Do I need to copy all PhysicsBody3D objects to the new physical space,

If your physics object are created as a node (CollisionObject2/3D or Joint2/3D) and want to move them from default space to your own space, please use PhysicsServer2/3D.xxx_set_space(), you don't need to copy them.

and all objects must be StaticBody3D to perform space_step() on a single object and obtain the correct collision?

There has not limit the type of physics objet.


I uploaded a testing project for reference.

Here is a rollback demo: physics_rollback_test.zip

Daylily-Zeleen avatar Sep 14 '23 09:09 Daylily-Zeleen

There has not limit the type of physics objet.

I may not be very clear. In fact, I hope to make a Step Physics Simulation in a certain object in the scene, but in order to get the correct interaction collision, I seem to need to copy a static scene in the simulation space.

Or turn PhysicsBody into staticbody in the original scene to prevent being simulated together

The following is the code that I tried to transfer the copy to the new physical space, but I still did not think about how to deal with the problem of retaining PhysicsBody and avoiding being simulated together.

class_name SubSpace3D
extends Node

@export var node_mirror: Node

var space_rid: RID
var space_rid_new: RID

func _ready() -> void:
# Body is to obtain the default space RID,maybe there is a better way?
	var Body = StaticBody3D.new()
	add_child(Body)
	space_rid = PhysicsServer3D.body_get_space(Body.get_rid())
	space_rid_new = PhysicsServer3D.space_create()

# Configure is a singleton to facilitate data calls from other locations
	Configure.space_rid = space_rid
	Configure.space_rid_new = space_rid_new

# Copy the basic data of the scene
	create(space_rid, space_rid_new)
	PhysicsServer3D.space_set_active(space_rid_new, true)
	Body.free()

# Nodes that need to be copied
	_update_space(node_mirror)

func _update_space(node:Node):
	var node_array: Array[Node]

	if node is RigidBody3D:
		node_array.push_back(node)

	for child in node.get_children():
		_update_space(child)

	for collisionobject in node_array:
		var node_new := collisionobject.duplicate()
		node.add_child(node_new)
		PhysicsServer3D.body_set_space(node_new.get_rid(),space_rid_new)

func _enter_tree():
	get_tree().connect("node_added",_node_added)
func _exit_tree():
	get_tree().disconnect("node_added",_node_added)

func _node_added(node:Node):
	await ready
	if is_ancestor_of(node) and node is CollisionObject3D:
		PhysicsServer3D.body_set_space(node.get_rid(),space_rid_new)

func set_gravity(space, new_space):
	var gravity: float = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_GRAVITY)
	PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_GRAVITY, gravity)

func set_gravity_vector(space, new_space):
	var gravity_vector: Vector3 = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR)
	PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, gravity_vector)

func set_linear_damp(space, new_space):
	var linear_damp: float = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_LINEAR_DAMP)
	PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_LINEAR_DAMP, linear_damp)

func set_angular_damp(space, new_space):
	var angular_damp:float = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_ANGULAR_DAMP)
	PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_ANGULAR_DAMP, angular_damp)

func get_direct_space_state(_active):
	return PhysicsServer3D.space_get_direct_state(_active)

func create(space, new_space):
	set_gravity(space, new_space)
	set_gravity_vector(space, new_space)
	set_linear_damp(space, new_space)
	set_angular_damp(space, new_space)

Az-qianchen avatar Sep 14 '23 12:09 Az-qianchen

@Az-qianchen Sorry, I don't think I can help you solve this specific application scenarios.

After reading your code, I found that you set the space_rid_new to actived. I'm not sure about your situation, but I think I need to clearify something here: A actived physics space will be stepped automatically by the main loop, if you want to control by yourself completely, you should ensure the physics space is Inactivated.

Daylily-Zeleen avatar Sep 14 '23 13:09 Daylily-Zeleen

A actived physics space will be stepped automatically by the main loop, if you want to control by yourself completely, you should ensure the physics space is Inactivated.

Oh, I was wrong about this

but it seems that rolling back for individual objects isn't that straightforward. Perhaps avoiding the approach of duplicating the space is to collectively roll back all PhysicsBody .

Before merging this PR into the main branch, maybe I'll try implementing the desired functionality by using a custom integrator along with manual calculations.

Thank you for your help.

Az-qianchen avatar Sep 15 '23 02:09 Az-qianchen

Sorry, I will ask the progress of this PR again

I used it for rigid body synchronization in my project Can this PR merge before the frozen 4.2?

https://github.com/godotengine/godot/assets/52212525/02765254-c593-4573-98ac-1e09956e8231

Az-qianchen avatar Sep 20 '23 06:09 Az-qianchen

I want predict projectile path in 3d with code like this:

static func predict_projectile_path(start_pos:Vector3, launch_velocity:Vector3,delta:float,stop_func:Callable,gravity:=Vector3.DOWN)->PackedVector3Array:
	var path := PackedVector3Array()
	var t := 0.0
	var index := 0
	var pos := start_pos
	var vel := launch_velocity
	var stop := false
	
	while not stop:
		stop = stop_func.call(index,pos,vel)
		if stop:
			break
		path.append(pos)
		index += 1
		t += delta
		vel = launch_velocity + gravity * t
		pos = start_pos + launch_velocity*t + gravity*pow(t,2)*0.5
	return path

GIF 2023-9-23 11-05-46

but it not work well and I don't know why. This pr like can solved my problem.

CsloudX avatar Sep 23 '23 03:09 CsloudX

@CsloudX

vel = launch_velocity + gravity * t
pos = start_pos + launch_velocity*t + gravity*pow(t,2)*0.5

pos does not take the changing vel into account. I think it could be something like

vel += gravity * delta
pos += vel * delta

But anyway, I think this is off-topic :P Using physics simulation for this is likely an overkill.

timothyqiu avatar Sep 23 '23 03:09 timothyqiu

but it not work well and I don't know why.

It seems that you have not consider the damping.

Daylily-Zeleen avatar Sep 23 '23 07:09 Daylily-Zeleen

vel += gravity * delta pos += vel * delta

Thanks, but the result was same.

CsloudX avatar Sep 24 '23 10:09 CsloudX

It seems that you have not consider the damping.

Thanks, and my linear damp was set to 0.

CsloudX avatar Sep 24 '23 10:09 CsloudX

PR progress? The only thing keeping it back is one doc change @Daylily-Zeleen

radiantgurl avatar Oct 06 '23 22:10 radiantgurl

I will say that the butterfly effect still counts for the rollbacking nature of this update, also since godot isnt deterministic i guess.

radiantgurl avatar Oct 07 '23 11:10 radiantgurl

@RadiantUwU A space which stepped by user should be deactivated (by space_set_activate()), otherwise it will be stepped by main loop again and bring this issue ( it means that query again). If step a space both by main loop and by user is expected, you should call space_flush_queries() again after space_step().

This is not elegent, and it seems that flush queries is Godot Physics specific, I'm not sure about this.

Daylily-Zeleen avatar Oct 07 '23 12:10 Daylily-Zeleen

NOTE: Collisions broken for rollbacks

radiantgurl avatar Oct 07 '23 12:10 radiantgurl

@RadiantUwU A space which stepped by user should be deactivated (by space_set_activate()), otherwise it will be stepped by main loop again and bring this issue ( it means that query again). If step a space both by main loop and by user is expected, you should call space_flush_queries() again after space_step().

This is not elegent, and it seems that flush queries is Godot Physics specific, I'm not sure about this.

Thank you!

radiantgurl avatar Oct 07 '23 12:10 radiantgurl

NOTE: Collisions broken for rollbacks

Can you provide a minimum demo for this issue?

Daylily-Zeleen avatar Oct 07 '23 12:10 Daylily-Zeleen

NOTE: Collisions broken for rollbacks

Can you provide a minimum demo for this issue?

Here minimum.zip use W to step forward, S to step backward Simulation runs at 7680 Hz btw

radiantgurl avatar Oct 07 '23 12:10 radiantgurl

NOTE: Collisions broken for rollbacks

Can you provide a minimum demo for this issue?

Here minimum.zip use W to step forward, S to step backward Simulation runs at 7680 Hz btw

Uh... you have not rollback anything at this demo. I need to clearify again. this pr just implements step physics simulation manually. Please refer this comment.

If you need to implement rollback feature, please refer my demo project (at the last of my first comment in this pr).

Daylily-Zeleen avatar Oct 07 '23 12:10 Daylily-Zeleen

Uh... you have not rollback anything at this demo. I need to clearify again. this pr just implements step physics simulation manually. Please refer this comment.

If you need to implement rollback feature, please refer my demo project (at the last of my first comment in this pr).

Thanks!

radiantgurl avatar Oct 07 '23 14:10 radiantgurl

Offtopic-ish - Ignorable: I am strongly interested in this feature (I am porting Rollback-Netcode from Unity to Godot). I see:

🔗 GHA / 🐧 Linux / Editor w/ Mono (target=editor) (pull_request) Failing after 20m

These are tests, right? Or is it about the review not yet done on this individual part? The linked log doesn't tell and I wonder why tests and review are combined here. I would expect that review is relevant at first when the tests are green.

tedbarth avatar Oct 09 '23 09:10 tedbarth

These are tests, right? Or is it about the review not yet done on this individual part? The linked log doesn't tell and I wonder why tests and review are combined here. I would expect that review is relevant at first when the tests are green.

It's occuring generally even for completely trivial PRs.


Strictly speaking, this pr just provide a way for simulating a physics space manully, and help you to realize rollback feature.

Note that this pr is not implement deterministic physic (Godot Physics is not deterministic), deterministic is a topic of physic engine and is not included in this pr (I mean that deterministic is not a duty of PhysicsServer).

Daylily-Zeleen avatar Oct 09 '23 09:10 Daylily-Zeleen

@Daylily-Zeleen Thanks for the elaboration. Is it a yes to the questions whether tests are failing? This is would be reason for blocking the merge, right?

tedbarth avatar Oct 09 '23 16:10 tedbarth

The class reference XML is outdated – @Daylily-Zeleen needs to run --doctool on a locally compiled build to update it, then push the changes.

Calinou avatar Oct 09 '23 18:10 Calinou