Decal image aspect ratio

Godot 4.3

I’m using decals to place posters on the walls of a 3D scene. They work fine, but I can’t maintain the aspect ratio of the image. In the editor, it has handles to stretch the extents of the decal but this deforms the image. Any ideas?

1 Like

2 options, use a script to enforce it or use Textured Quads Instead.

Create a script attached to your decal that enforces the aspect ratio.

@tool
extends Decal

@export var texture: Texture
@export var maintain_aspect_ratio: bool = true
@export var width: float = 1.0 : set = set_width

func _ready():
    if texture and maintain_aspect_ratio:
        update_size()

func set_width(new_width):
    width = new_width
    if texture and maintain_aspect_ratio:
        update_size()

func update_size():
    var aspect_ratio = float(texture.get_width()) / float(texture.get_height())
    var height = width / aspect_ratio
    extents = Vector3(width / 2, height / 2, extents.z)

Use Textured Quads Instead. For posters specifically, consider using simple flat meshes with textures:

Create a QuadMesh or PlaneMesh and Apply your poster texture to the material. Position it slightly offset from the wall to avoid z-fighting. This gives a more full control over proportions and often looks better for flat objects like posters.

1 Like

This is awesome. Thanks!

There is a small problem on line 20 (see below). I checked the docs and Decal doesn’t have a property “extents”, but it does have “size”.

1 Like

Sorry, I primarily program in C# and wasn’t testing before I sent it lol. You are right its size.

@tool
extends Decal

@export var texture: Texture
@export var maintain_aspect_ratio: bool = true
@export var width: float = 1.0 : set = set_width

func _ready():
    if texture and maintain_aspect_ratio:
        update_size()

func set_width(new_width):
    width = new_width
    if texture and maintain_aspect_ratio:
        update_size()

func update_size():
    var aspect_ratio = float(texture.get_width()) / float(texture.get_height())
    var height = width / aspect_ratio
    size = Vector3(width, height, size.z)

this should work now. Sorry bout that.

Okay, it works - except for 1 more problem:

Decals project their image from -Y local direction. The script seems to be maintaining the ratio but its from the +Z direction (the bottom box shown with the red arrow) where the image doesn’t project from.

1 Like

You see, thats because im a big dummy and im writing code at 420 am my time lol

Godot Decals project along their negative Y axis , not along Z as my script was assuming.

func update_size():
    var aspect_ratio = float(texture.get_width()) / float(texture.get_height())
    # The height affects the Z dimension since decals project along -Y
    var depth = width / aspect_ratio
    # Keep Y dimension for projection depth, adjust X and Z for aspect ratio
    size = Vector3(width, size.y, depth)
1 Like

Huh, Weird. Sorry i have work in like 3 hours and I haven’t slept much. I can try and figure this out when I get to work. Id say just throw stuff at the wall and see if it sticks?

It works! I just had to close and re-open the scene.

Thanks for your help on this!

1 Like

The script works great! Here is the complete code if anyone is interested:

# script used to maintain decal image aspect ratio
# add this script to a decal node
# you may need to reload the scene to get it working

@tool
extends Decal

@export var texture: Texture
@export var maintain_aspect_ratio: bool = true
@export var width: float = 1.0 : set = set_width

func _ready():
	if texture and maintain_aspect_ratio:
		update_size()

func set_width(new_width):
	width = new_width
	if texture and maintain_aspect_ratio:
		update_size()

func update_size():
	var aspect_ratio = float(texture.get_width()) / float(texture.get_height())
	# The height affects the Z dimension since decals project along -Y
	var depth = width / aspect_ratio
	# Keep Y dimension for projection depth, adjust X and Z for aspect ratio
	size = Vector3(width, size.y, depth)
1 Like