godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.07k stars 68 forks source link

Return the surface material that was hit by a RayCast #3909

Open elvisish opened 2 years ago

elvisish commented 2 years ago

Describe the project you are working on

A first person game using Trenchbroom maps/Qodot as level editor.

Describe the problem or limitation you are having in your project

Qodot imports levels as one big MeshInstance with hundreds of colliders, as such each material is a surface material of the MeshInstance. I need a way of being able to detect a specific surface material at a particular point (such as under the player).

Describe the feature / enhancement and how it helps to overcome the problem or limitation

For different footstep sounds, it would be easiest if I could compare the surface material and play the appropriate sound effect (grass, rock, sand, etc). I can't just cast a ray down and check as there's only one mesh for the level, but many surface materials assigned to this MeshInstance.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Integrated into Raycast that can access the surface material at the point the ray hits.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Not that I know of, I'd be interested to know if possible.

Is there a reason why this should be core and not an add-on in the asset library?

I think it requires deeper integration, but I could be wrong.

Calinou commented 2 years ago

Related to https://github.com/godotengine/godot-proposals/issues/2198.

I don't think it's possible to get the actual mesh material from a RayCast, as colliders will not always match the mesh geometry (they often need to be simplified for gameplay or performance reasons). You could however get the PhysicsMaterial associated to a PhysicsBody.

Since each PhysicsBody can only have one PhysicsMaterial assigned to it, I'd recommend creating several PhysicsBodies, one per surface type. This could be done by splitting surface types into different PhysicsBodies on import.

elvisish commented 2 years ago

You could however get the PhysicsMaterial associated to a PhysicsBody.

Since each PhysicsBody can only have one PhysicsMaterial assigned to it, I'd recommend creating several PhysicsBodies, one per surface type. This could be done by splitting surface types into different PhysicsBodies on import.

A little difficult I think currently as Qodot handles all of the conversion from.map into nodes, unless I'm mistaken and it's a relatively trivial job to automate at runtime? I'm not even sure how surface types would factor in here as they're just materials as set in Trenchbroom that are automatically applied to whichever part of the mesh they need to be.

Calinou commented 2 years ago

If you just want to get something working quickly, you could also add Area nodes that "tag" specific surfaces contained within the Area. When the footstep event occurs within the Area node, use a different footstep sound/effect. This could also be extended to gameplay events (such as specific surfaces negating fall damage).

elvisish commented 2 years ago

If you just want to get something working quickly, you could also add Area nodes that "tag" specific surfaces contained within the Area. When the footstep event occurs within the Area node, use a different footstep sound/effect. This could also be extended to gameplay events (such as specific surfaces negating fall damage).

Thanks, I think that's the approach I'll have to take for now. It would be immensely useful to be able to grab a surface material with a ray though, to avoid having to manually define areas (with Trenchbroom, layers can also be used to separate sections up with might be useful in the meantime until surface materials at point can be grabbed).

Zireael07 commented 1 year ago

Related: https://github.com/godotengine/godot-proposals/issues/2863

Gamepro5 commented 1 year ago

Here's an example of a 2004 game doing something similar in order to fetch the data needed to know what decal to spawn. I would also love something like this to be implemented.

https://github.com/godotengine/godot-proposals/assets/23440295/38e721f3-a065-41b9-850c-daedec531fdb

Okxa commented 10 months ago

Seems like current workarounds involve iterating on the mesh vertex data and then using Geometry.ray_intersects_triangle to check if this face is the current one.

There seems to be a couple of ways to iterate upon mesh vertices, I have not tested their performance relative to each other:

In both cases you end up iterating on mesh data, which can be expensive when the mesh is complex, as seen in the video.

Therefore native implementation could be preferred. (Though I cannot say how performant that would be anyways, I am not familiar with how raycasts work at low level.)

EDIT: More thougths:

As for aforementioned source engine, it only checks for material surface properties on world geometry (== brushes). Models have a single physical property defined at model compile time, even if they have multiple materials. Not sure, but it might be a conscious decision to implement the surface properties that way to avoid checking surfaces on complex models to save on performance.

So if this (raycasting for material) would possibly be performance intensive operation (depending on how detailed the mesh is) when implemented in godot engine, then of course proper warnings should be documented.

alexjhetherington commented 2 months ago

I support this proposal! In addition to iterating on the mesh data, as above, there are some other solutions:

You can see examples of both here: https://github.com/alexjhetherington/godot-identifying-materials-examples?tab=readme-ov-file

solitaryurt commented 1 month ago

This would be useful for footsteps and bullet impact effects.