Is there no way to update the texture of an instance of a game object?

I’m generating a 3D map of cubes programmatically by looping over a 2D tilemap, and using a factory to generate each cube, and so far so good. However I can’t seem to find a way to set the texture of the individual cubes. The only solution seems to be to create N models (each with it’s own texture), N game objects (for each model), and N factories (for each object)… and then to use a lookup table. This seems like a ALOT of overhead. Is there something I’m missing?

Set the texture on the model component directly. From google:

1. Setting Textures on Models and Meshes:

To set a texture on a model or mesh component at runtime, you can use go.set() to modify the textureN property of the component, where N corresponds to the texture unit (e.g., texture0, texture1).

-- In a script component attached to the game object with the model/mesh
function init(self)
    -- Assuming "my_texture.png" is an image in your project and has been added to an atlas or created dynamically
    local my_atlas_resource = resource.atlas("/my_atlas.atlas") -- If using an atlas
    local atlas_texture_hash = hash(my_atlas_resource.texture) -- Get the texture hash from the atlas

    -- Set the texture on the model/mesh component at the specified URL
    -- Replace "#model" or "#mesh" with the actual ID of your component
    go.set("#model", "texture0", atlas_texture_hash) 
    -- Or if you created a texture dynamically:
    -- local dynamic_texture_id = resource.create_texture("/my_dynamic_texture.texturec", texture_params)
    -- go.set("#model", "texture0", dynamic_texture_id)
end
1 Like

Gave that a shot, didn’t work. Did figure out how to expose a resource property, which would allow for design-time changes, but not runtime changes.

Did a deep dive, and this looks like I’m going to need to learn vertex and fragment stuff in order to read the texture info from the tile source and update it within the material (seems pretty complex).

I was really hoping for an easier way to simply set the texture of the cube upon instantiation.

Did you get an error? Or did nothing happen?

Note that resource properties must be declared at global scope.

go.property("my_atlas_resource", resource.atlas(“/my_atlas.atlas”))

function init(self)
    print(self.my_atlas_resource) – prints the resource *hash*
end

Also note that a resource is a hash, not a struct. This is not valid: my_atlas_resource.texture

1 Like

So this is the script I have right now (attached to my cube.go):

go.property("cube_texture", resource.texture("/asset/image/brown.png"))

function init(self)
  go.set("#cube", "texture0", self.cube_texture)
end

and this allows me to, at design time, change the cube’s texture, and as expected, on build, it generates just the single texture (i.e. brown.texturec, or whatever image I have chosen). I could not find a way to pass in a resource.texture during creation of the object via my factory.

preferably I would like to pass in the index of the current tile (which I already have):

go.property("tilesource", resource.tile_source("/map/map.tilesource"))
go.property("index", 0)

function init(self)
--[[
  
  local cube_texture = lookup the texture in tilesource by the given index

]]--
  go.set("#cube", "texture0", cube_texture)
end

but so far I have not been able to figure that out, likewise I have also tried (using a lookup table to determine the Id of the image in the atlas from my tile source index, which once again I already have):

go.property("atlas", resource.atlas("/atlas/map.atlas"))
go.property("id", "brown")

function init(self)
--[[
  
  local cube_texture = lookup the texture in atlas by the given id

]]--
  go.set("#cube", "texture0", cube_texture)
end

but I also can not figure out how to do that lookup either.

If anyone can help me figure that out, it would be far easier than trying to learn the render pipeline and GLSL (though eventually I will want to do at some point, just not now).

I have found that I can do this:

--Without texture properties, hash(texture_path) will not resolve
go.property("cube_texture0", resource.texture("/asset/image/0.png"))
go.property("cube_texture1", resource.texture("/asset/image/1.png"))
go.property("tile_index", 0)

function init(self)
  local texture_path = "/asset/image/" .. self.tile_index .. ".texturec"
  go.set("#cube", "texture0", hash(texture_path))
end

but, this only works if I have a property per texture, I have tried to copy across the .texturec files that are generated during build into another folder, and use a custom resource path in my project, but it appears that is a no go. Despite the .texturec files being properly copied, referencing the path is null and void if there is no associated property with a matching path to the .png file.

Clearly I’m not knowledgeable enough, yet, to make an informed solution to this issue. I’m going to keep digging, but any insight or assistance would be greatly appreciated.

Correct, you need a property per texture. If that isn’t an option for one reason or another you can create the texture at runtime or update an existing texture with new image data. Have a look at the Create Atlas example and focus on the create_texture() function:

@britzl Okay… I “think” I’m getting closer to what I want.

Please ignore the terrible overhead in this first draft POC (I obviously will need track each newly created texture and not read from disk every single time I create a cube, maybe even pre-create these in my main script)

P.S. I’d love to figure out how to read the PNG data directly from my tilesource and avoid all these extra resource files entirely.

Anyways I currently now use the defold-png extension (which apparently you wrote, thank you!) to load a custom PNG resource for each texture and set it accordingly.

--cube.script attached to cube.go which modifies cube.model (i.e. #cube)

--Current tilesource index gleamed from tilemap
go.property("tile_index", 0)

function init(self)
  local file = "/asset/image/" .. self.tile_index

  -- Load a PNG from disk
  local f = sys.load_resource(file .. ".png")

  -- Decode the PNG to an RGBA buffer
  local buf, w, h = png.decode_rgba(f)

  -- Get texture's resource path
  local resource_path = go.get("#cube", "texture0")

  -- Setup texture parameters
  local header = {
    width = w,
    height = h,
    type = resource.TEXTURE_TYPE_2D,
    format = resource.TEXTURE_FORMAT_RGBA,
    num_mip_maps = 1
  }

  --quick and dirty ignore for duplicate textures
  local success, result = pcall(function()
    resource.create_texture(file .. ".texturec", header, buf)
  end)
  go.set("#cube", "texture0", hash(file .. ".texturec"))
end

and this is working, so my understanding is growing… now to dig even deeper :slight_smile:

1 Like