Nonexistent function lock() in base Image, when trying to parse a heightmap image

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Macryc

Hi Guys

In Godot 4, I’m trying to build a mesh from a heightmap. Using the code below, I trying to parse the image, which is a noise image.

I keep getting the error 'Nonexistent function ‘lock()’ in base Image.
When I go var heightmap = noiseImage.get_data() instead, i get the same error, only it reads: Nonexistent function lock() in base PackedByteArray. What am I doing wrong?

func _ready():
	var noiseImage = Image.new()
	noiseImage.load("res://231.jpg")
	width = noiseImage.get_width()
	height = noiseImage.get_height()
	var heightmap = noiseImage
	heightmap.lock()
	for x in range(width):
		for y in range(height):
			heightData[Vector2(x,y)] = heightmap.get_pixel(x,y).r*20
	heightmap.unlock()

The lock/unlock methods have been removed in Godot 4.0. Is there any issue if you just remove them?

Ninfur | 2022-05-05 18:15

Weird, they’re still in the docs.

Anyway, removed them and the code runs through but no geometry gets created.

Also, I can see an error in debug at the line which commits the mesh, the error reads: E _ready: Required virtual method Mesh::_get_surface_count must be overridden before calling.

I’m using this code I found online, which makes sense and ‘should work’ when you read it:

extends Node3D

@onready var width;
@onready var height;

var heightData = {}

var vertices = PackedVector3Array()
var UVs = PackedVector2Array()
var normals = PackedVector3Array()

var tmpMesh = Mesh.new()

func _ready():
	var noiseImage = load("res://231.jpg")
	width = noiseImage.get_width()
	height = noiseImage.get_height()
	var heightmap = noiseImage

	for x in range(0,width):
		for y in range(0,height):
			heightData[Vector2(x,y)] = heightmap.get_pixel(x,y).r*20
	
	# generate terrain
	for x in range(0,width-1):
		for y in range(0,height-1):
			createQuad(x,y)
	
	var st = SurfaceTool.new()
	st.begin(Mesh.PRIMITIVE_TRIANGLES)
	st.set_material(load("res://new_standard_material_3d.tres"))
	
	for v in vertices.size():
		st.set_color(Color(1,1,1))
		st.set_uv(UVs[v])
		st.set_normal(normals[v])
		st.add_vertex(vertices[v])
	
	st.commit(tmpMesh)
	$MeshInstance3D.mesh = tmpMesh


func createQuad(x,y):
	var vert1 # vertex positions (Vector2)
	var vert2
	var vert3	
	var side1 # sides of each triangle (Vector3)
	var side2	
	var normal # normal for each triangle (Vector3)
	
	# triangle 1
	vert1 = Vector3(x,heightData[Vector2(x,y)],-y)
	vert2 = Vector3(x,heightData[Vector2(x,y+1)],-y-1)
	vert3 = Vector3(x+1,heightData[Vector2(x+1,y+1)],-y-1)
	vertices.push_back(vert1)
	vertices.push_back(vert2)
	vertices.push_back(vert3)
	
	UVs.push_back(Vector2(vert1.x/10, -vert1.z/10))
	UVs.push_back(Vector2(vert2.x/10, -vert2.z/10))
	UVs.push_back(Vector2(vert3.x/10, -vert3.z/10))
	
	side1 = vert2-vert1
	side2 = vert2-vert3
	normal = side1.cross(side2)
	
	for i in range(0,3):
		normals.push_back(normal)
	
	# triangle 2
	vert1 = Vector3(x,heightData[Vector2(x,y)],-y)
	vert2 = Vector3(x+1,heightData[Vector2(x+1,y+1)],-y-1)
	vert3 = Vector3(x+1,heightData[Vector2(x+1,y)],-y)
	vertices.push_back(vert1)
	vertices.push_back(vert2)
	vertices.push_back(vert3)
	
	UVs.push_back(Vector2(vert1.x/10, -vert1.z/10))
	UVs.push_back(Vector2(vert2.x/10, -vert2.z/10))
	UVs.push_back(Vector2(vert3.x/10, -vert3.z/10))
	
	side1 = vert2-vert1
	side2 = vert2-vert3
	normal = side1.cross(side2)
	
	for i in range(0,3):
		normals.push_back(normal)

Macryc | 2022-05-05 18:53

Did you find any solution?

zkmark | 2023-01-13 18:35