Add transparency to a 3d .obj/.mtl model?

@John I did not manage to find a way to satisfactorial add transparency to a 3D .obj/.mtl model?

If the .material property is defined, one can use material.opacity to add transparency (with material.blendMode=NORMAL).
But if .material is defined it seems to overwrite the loaded .mtl information and the object will be a single colour, so one loses the different colours defined by the .mtl file.

A related question…is there a way to undo a .material property definition, i.e. remove a previously declared material from a model and come back to using the original .mtl information?

I am probably a bit confused how this all works!

@piinthesky - probably way out of left base, but - is the way to do this by defining each vertex colour as transparent for the model?

@Bri_G i tried what you suggested but did not succeed-maybe i did something wrong. I think the vertex colors are not what the .mtl uses-but i am not sure.

@piinthesky - you can colour an object by texturing using an image or by assigning a colour to each vertex. Are your model faces a uniform colour or are they a part of multicoloured texture?

it is a multi-colour .mtl file. Like the starwars tiefighter we used in the past or the kenny figures in the 3d object viewer example.

@piinthesky - have you tried changing the opaqueness with the a parameter in

myModel:color( index, r, g, b, a )

for each of the vertices?

@Bri_G yes that is essentially what i tried. If you can get it to work let me know!

@piinthesky If you load the model from a file you can use getMaterial(index) (sorry didn’t realise it was undocumented) to get each individual part of the model that uses a separate material. You can then set any of the options on that material. If you try getting a material at an invalid index it might crash though :open_mouth:

@John that works! I was confused about what the index was, but then realised it corresponds to the number of the materials used, if there are 2 materials then index =1,2. We also need a getNumberOfMaterials.

@John if i have many instances of the same obj/mtl model when i modify the material properties of one instance it affects all instances. This seems to be a different behaviour from the standard craft model-in this case one can modify the material of each instance independently.

Of course, for each instance i could load in a new version of the same model, but that quickly gets expensive in terms of memory and speed for large models.

Or am i confused!?

@piinthesky Ah I see what you mean. This is an oversight I suppose. You can use entity:get(craft.renderer).material = myMaterial to have a custom material per entity while using the same model. You can use Standard or Specular with .blendMode = NORMAL and .opacity = desired_value and set other values taken from the original material with a new one created per entity you want to have a unique look.

The only problem is it only gives you the one material (whatever the first indexed material is). You could export models so that the first material is the one you want to change, unless you need to change multiple ones. I’ll have to look into how we can have multiple custom materials per entity.

@John i observe two different modes for an obj with multiple mtl and then having multple instances of it:

  1. i can set the material.diffuse then all the mtls will become the same colour. i can set a different overall colour for each instance.

  2. i can set the material(n) of each material and have multple colours on the model, BUT each instance will be the same!

i am unable to have multiple instances with different colours from each other for each mtl

@piinthesky Yeah I think you’ve run into an edge case that I did not consider (sorry), I’ll try my hand at making an example project that does what you want (multiple instances of the same model with multiple sub-meshes each having their own unique materials) and update the API to reflect the changes required.

The api would probably look something like this:

-- Create a randomly coloured material
function randMaterial()
    local mat = craft.material(asset.builtin.Materials.Standard)
    mat.diffuse = color(math.random(0,255), math.random(0,255), math.random(0,255))
end

-- Load a model with 3 submeshes (each can each have a unique material)
mdl = craft.model(asset.SomeTripleMaterialModel)

-- Create 1000 models and assign 3 random materials to each one
for i = 1,1000 do 
    local mat1 = randMaterial()
    local mat2 = randMaterial()
    local mat3 = randMaterial()
    local e = scene:entity()
    e.x = i
    e.model = mdl
    e.materials = {mat1, mat2, mat3}
end

There would also be something like e:setMaterial(index, mat) but the .materials property would be a shortcut. The entity material would override any materials on the model itself (including beyond the first submesh if you set them).

Does this sound like it would solve your issues?

If you really need this functionality right away you’ll have to split your model up into single material parts, then add each one as a separate child entity, and assign the entity.material property for each thing you want to have a unique colour per instance.

Ok, I’ve added .materials and :setMaterial(m, index) to the renderer component in craft. I did a test using the same model 100 times with various random colours for each sub mesh. Seems to work fine so far:

@John, brilliant! a getNumberOfMaterials() might also be useful.

@John - gotta say, from the image, that looks superb. Few questions

  1. With so many models on display what is the effect on FPS ?
  2. In the Tool window you refer to setting the mesh using the Craft.model(asset) - is this a way of combining mesh use with Craft ?
  3. You also refer to using assetList to identify all the models in the asset source. Have you got a simple example of this that you could share with us ?

As ever your work is excellent (but usually whizzing over my head). Thanks.

@piinthesky model.submeshCount can be used to determine the number of submeshes in the model, which is the same as the number of materials

@Bri_G

  1. It’s not the most efficient thing but using the same model each time does help reduce performance impacts (I didn’t notice any fps drops on my iPad Pro 2018 model)
  2. mesh and model are still technically incompatible, In Codea 4.0 we will have a new runtime with a streamlined API, folding all duplicate types together, so there will only be mesh, shader and material which will all work with each other
  3. assetList() is now deprecated as it uses the old asset system, the way to do it now is:
-- Load all models in spaceKit asset library
local spaceKitAssets = asset.builtin.SpaceKit.all
local spaceKitModels = {}
for k, v in pairs(spaceKitAssets) do
    if v.type == MODELS then -- make sure to filter out any non-model assets
        spaceKitModels[v.name] = craft.model(v)
    end
end

This works with builtin assets, documents, dropbox, the current project and any subfolders as well. So you can have a folder Documents/Models/Widgets/... and use asset.documents.Models.Zombies.all if you want. Technically it also works with projects as well but it can be tricky to make subfolders in them right now.

@John - thanks for the feedback and clarification of assetList(). Looking forward to Codea 4.0.

@John i did not succeed to get your multiple materials to work? I followed the prescription you gave earlier in this post. Is the builtin asset Blocky_Characters.Man a triple material model? In your code above should the function randMaterial() have return mat at the end?