Material overlay
Asked Answered
I

8

0

I'm currently exploring the material overlay property as a potential solution to a goal in my game. As I understand, this is a relatively new feature added to Godot and I'm not seeing any tutorials online about it. What I want to do I place an icon on the floor beneath my character that indicates his position. The character moves on a grid in-game and I want this visual to emphasize that. So I'm thinking the proper way to do this is to overlay my icon on the mesh material at the character position. However this should sort of project downward as if it was being cast from above so that it works on slopes or stairs too. So far I've put a function in the player script that assigns a shader script to any instantiated nodes that I've assigned to the "ground" group (so that the icon can only be cast onto the floor and not any thing else.) Other than that I'm at a loss of what to do.
Am I going about this correctly? Or if there's a better approach could someone point me in the right direction? I'm not familiar with glsl script and I'm not exactly sure about how I can get a new texture to overlay on a mesh's texture but I suspect I should use the mix function somehow.

Illomened answered 8/12, 2023 at 5:14 Comment(0)
T
0

Illomened You can assign a material with alpha transparency to material_overlay property.

Traceable answered 8/12, 2023 at 7:48 Comment(0)
I
0
func draw_square():
var location = Vector3(round(self.position.x), 0.0, round(self.position.z))
var stored = false
if location != location_stored:
    location_stored = location
    if is_instance_valid(square):
        print("delete")
        square.queue_free()
        drawn = false
if drawn != true:
    make_square()
    print("make_square")
    drawn = true

func make_square():
var space_state = get_world_3d().direct_space_state
var square_ray = PhysicsRayQueryParameters3D.create(self.position, Vector3(self.position.x, self.position.y - 30, self.position.z))
var square_cast = space_state.intersect_ray(square_ray)
var square_location = square_cast.position.snapped(Vector3.ONE)
var neg_z_ray = PhysicsRayQueryParameters3D.create(Vector3(round(self.position.x), self.position.y + 1, round(self.position.z) - 0.49), Vector3(round(self.position.x), self.position.y - 30, round(self.position.z - 0.49)))
var pos_z_ray = PhysicsRayQueryParameters3D.create(Vector3(round(self.position.x), self.position.y + 1, round(self.position.z) + 0.49), Vector3(round(self.position.x), self.position.y - 30, round(self.position.z + 0.49)))
var neg_x_ray = PhysicsRayQueryParameters3D.create(Vector3(round(self.position.x) - 0.49, self.position.y + 1, round(self.position.z)), Vector3(round(self.position.x) - 0.49, self.position.y - 30, round(self.position.z)))
var pos_x_ray = PhysicsRayQueryParameters3D.create(Vector3(round(self.position.x) + 0.49, self.position.y + 1, round(self.position.z)), Vector3(round(self.position.x) + 0.49, self.position.y - 30, round(self.position.z)))
var neg_z_cast = space_state.intersect_ray(neg_z_ray)
var pos_z_cast = space_state.intersect_ray(pos_z_ray)
var neg_x_cast = space_state.intersect_ray(neg_x_ray)
var pos_x_cast = space_state.intersect_ray(pos_x_ray)


var scene_root = get_tree().root.get_children()[0]
square = MeshInstance3D.new()
var icon = QuadMesh.new()
square.mesh = icon
square.position = square_location + Vector3(0,0.01,0)
if neg_z_cast != {} && pos_z_cast != {}:
    neg_z_location = neg_z_cast.position
    pos_z_location = pos_z_cast.position
    square.rotation.x = atan(neg_z_location.y - pos_z_location.y - 90)
if neg_x_cast != {} && pos_x_cast != {}:
    neg_x_location = neg_x_cast.position
    pos_x_location = pos_x_cast.position
    square.rotation.z = atan(neg_x_location.y - pos_x_location.y)
var square_material = StandardMaterial3D.new()
square_material.set_texture(StandardMaterial3D.TEXTURE_ALBEDO, icon_texture)
square_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
icon.material = square_material
if square_cast.collider.is_in_group("ground"):
    scene_root.add_child(square)

Material overlay might not be the correct solution for my problem. What i'm doing is simply creating a meshinstace3D quadmesh and instancing it at the player postion. This generally works correctly except for on slopes. If you look at my code, what I'm doing is attempting to shoot 4 rays down and find their positions where they collide with the floor. I then use trigonometry to determine the angles and rotate by those angles. But this doesnt seem to work and I'm not sure why.

I also get weird glitches every now and then when I first load the game. For some reason once I get a glitch one time, I won't get any more, but for example there might be an issue where this quadmesh does not appear at one space for some reason, but it will only happen one time and never again.

Illomened answered 9/12, 2023 at 4:22 Comment(0)
I
0

Traceable The problem with the material overlay is that when I apply a texture it simply repeats it across the entire UV of the object, when instead I just wanted to position a small texture at specific spots on the mesh. Another option I explored was the new projection feature in the spotlight but this didn't work very well.

Illomened answered 9/12, 2023 at 4:24 Comment(0)
I
0

Really, I was hoping that the light projector would work out because it made the most sense. If for example, you're standing on stairs, the icon beneath your player should look as if it was projected onto the stairs, which a quadmesh would not be able to do. A good example of what I'm talking about are the selection icons you see under npcs in WoW and other MMOs when you click on them. You would think there would be an easy way to accomplish this but I'm beginning to think its not possible with all the research I've done.

Illomened answered 9/12, 2023 at 4:34 Comment(0)
I
0

var square_location = Vector3(round(square_cast.position.x), square_cast.position.y, round(square_cast.position.z))

I found the issue with my code and replaced my .snapped with a vector3 that rounds the x and z but leaves the y alone. Because I'm moving the player on a grid, many features have to be rounded but I can't often use snapped because I don't want the y position to snap. I'm in fact using the light projector (I found that if you max out the specular it works fairly well). My only issue remaining is to figure out how to rotate the light according to slopes, or perhaps a better solution would be to find a way to adjust the projector so that it always projects at the same size, regardless of the distance to the surface.

This is a picture of the icon I'm now casting below the player, which looks pretty close to the desired effect.

Illomened answered 9/12, 2023 at 4:56 Comment(0)
T
0

Illomened The simplest way is to use the material overlay with an orthographically projected texture in world space. It'd require writing a custom shader though.

Traceable answered 9/12, 2023 at 9:27 Comment(0)
T
1

Traceable In fact the easiest non-shader way is to use a Decal node parented to the player.

Traceable answered 9/12, 2023 at 10:42 Comment(0)
I
0

Traceable

Thank you. This is exactly what I needed.

Illomened answered 9/12, 2023 at 16:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.