Christopher Bennett
Christopher Bennett

Reputation: 801

Godot 3.1 telling me that lock() and get_pixel() are nonexistent functions

I'm attempting to generate a height map from a noise texture. As far as I understand, in order to call get_pixel() on an image in this context, the image must first be locked. However, when I attempt to run the program, it exits with the error: Invalid call. Nonexistent function 'lock' in base 'StreamTexture'.

If I attempt to run it without locking the image, I get the error: Invalid call. Nonexistent function 'get_pixel' in base 'StreamTexture'.

I am certain that the instructions that I am following are for the same version of Godot I am running (3.1), so why is the engine telling me that lock() and get_pixel() are nonexistent functions?

My code is here:

extends Spatial

var width
var height
var heightData = {}
var vertices = PoolVector3Array()
var drawMesh = Mesh.new()

func _ready():
    var noiseTexture = load("res://noiseTexture.png")
    width = noiseTexture.get_width()
    height = noiseTexture.get_height()

    noiseTexture.lock()
    for x in range(0, width):
        for y in range(0, height):
            heightData[Vector2(x,y)] = noiseTexture.get_pixel(x,y).r
    noiseTexture.unlock()

    for x in range(0, width-1):
        for y in range(0, height-1):
            createQuad(x,y)

    var surfTool = SurfaceTool.new()
    surfTool.begin(Mesh.PRIMITIVE_TRIANGLES)

    for i in vertices.size():
        surfTool.add_vertex(vertices[i])

    surfTool.commit(drawMesh)
    $MeshInstance.mesh = drawMesh

func createQuad(x,y):
    #First half
    vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
    vertices.push_back(Vector3(x, heightData[Vector2(x,y+1)], -y-1))
    vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
    #Second Half
    vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
    vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
    vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y)], -y))

Any help is greatly appreciated.

EDIT - I have (tried) to implement the changes that were suggested in the comments (yet I still don't know what to do with the color variable) and have attached a screenshot of my resulting code, as well as some comments I have made to try and explain to myself why the process SHOULD be working (I think). It also shows my node structure, which is why I opted to display this as an image. However, when I try to run this, the program crashes with the error displayed.

HeightMap Fail

Upvotes: 3

Views: 2007

Answers (2)

Daniklad
Daniklad

Reputation: 1079

I have run into similar issues with generating heightmap terrains.

nathanfranke is correct and his solution will work.

If you for some reason use an ImageTexture you can call get_data() on that to get the underlying Image. Then you call lock() on the Image just like nathan says in his answer.

Take care to check that your coordinates for get_pixel() are correct. You can set a breakpoint by clicking on the very left edge of the line of code. I mention this because I was very frustrated until I realized that my coordinate calculations were all int which resulted in the sampled pixel always being at <0,0>.

Here is part of my code for sampling an image into the HeightMapShape.map_data for Bullet heightmap collisions:

var map_w = shape.map_width
var map_h = shape.map_depth
var img_w = img.get_width()
var img_h = img.get_height()
img.lock()

for y in range(0, map_h):
    py = float(img_h) * (float(y) / float(map_h))

    for x in range(0, map_w):
        px = float(img_w) * (float(x) / float(map_w))
        index = y * img_h + x
        pix = img.get_pixel(px, py)
        shp.map_data[index] = pix.r * heightmap_height + heightmap_offset

Upvotes: 0

nathanfranke
nathanfranke

Reputation: 991

Check the docs; StreamTexture does not have the method lock.

I think the class you are looking to use is Image. The Texture class is typically intended for drawing on the screen or applying to a Material

var noiseImage = Image.new()
noiseImage.load("res://noiseTexture.png")
noiseImage.lock() # Lock the image here
var color = noiseImage.get_pixel(10, 10) # Replace with your height map population

PS:

Just to let you know, I had a lot of issues with memory usage here so make sure you test that also (C# has bad garbage collector though). You might need to dispose of the image, surface tool, and array mesh (If you remove the terrain object) to maintain optimum performance.

Upvotes: 3

Related Questions