Material Instances and matobject

In the previous tutorial it was already mentioned that materials can be created "from string" or "from .mtl file". This time we will use .mtl files which also give us the benefit of defining parameters that are unique for each mesh using the material. With the matobject interface we will manipulate each instance of the spaceship to have custom specularity.

The .mtl material

Similar to the shader, is the .mtl material fileformat. For more details and what you can do see the MTL reference. Typically you would define one SHD shader to use and various textures that will fill the texture slots in the used shader. Only the textures really used by the Shader will be loaded. You can also add texture matrix effects such as rotation, scaling and moving. With the matobject interface you can access those control values (matcontrolid, mattexcontrolid]) and the "timer" value of the floatmod(ifiers).

luxinia_Material_v110

Shader{    // just Shader is same as Shader:0, 
        // which always has to be defined
    SHD "3tex_diffspecillum.shd";
        // the shader we want to use

    control "specularctrl" 0 4;    
        // control objects and modifiers allow us to give
        // each instance that uses this material unique values

        // we only want to control all values of the next
        // controllable value
        // 0 is the start, 4 the length

    param "specularctrl" (1,1,1,8);
        // we make this parameter inside the shader unique for this 
        // material and initialize it with a default value
}
Texture:0{
    TEX "t33comb.png";    // the combined texture that holds 
                        // specular in the alpha channel
}
Texture:1{
    TEX "t33diff.png";    // only diffuse
}
Texture:2{
    TEX "t33spec.png";    // only specular
}
Texture:3{
    TEX "t33illum.png";    // emissive
}

In the material above, if we would have just define the "param" inside the Shader-stage, then the parameter would be the same for all surfaces that use this material. With the "control" command, we make it unique per instance.

As long as no matobject commands are used on an object that uses the material, no additionally memory is needed. But once you apply such a command, a "matobject" instance is allocated for this mesh, and the memory needed is influenced by how many control values you used. Be aware that the matobject becomes invalid when the matsurface assignment for the object changed. So you need to reapply control values if needed after matsurface assignments (just like with the renderflags).

Scene setup

Once we applied the material as in the previous Specular Shader tutorial, we create some spaceships and give each different specularity.

-- load the resources we need
local t33 = model.load("t33.f3d")
local mtl = material.load("t33.mtl")

-- now we first retrieve the matcontrolid we need later.
local specctrlid = mtl:getcontrol("specularctrl")

-- as we want more than one ship lets use a function
function makespaceship(pos,specctrl)
    local actor = actornode.new("t33",0,0,0)

    actor.l3d = l3dmodel.new("t33",t33)
    actor.l3d:linkinterface(actor)
    actor.spos = pos

    -- for all meshids 
    for i=1,t33:meshcount() do
        -- create a meshid 
        local mid = t33:meshid(i-1)    -- meshid indices start at 0
        -- assign material to l3dmodel's mesh
        actor.l3d:matsurface(mid,mtl)

        -- give each spaceship a unique specctrl value using the
        -- matobject interface command
        -- we also need to hand over the meshid as well
        actor.l3d:moControl(mid,specctrlid,unpack(specctrl))
    end
    -- always set Renderflags AFTER material assignments
    actor.l3d:rfLitSun(true)

    return actor
end

-- lets create some spaceships
spaceships = {}
table.insert(spaceships,makespaceship({-3,15,0},{1,1,1,8}))

-- more intense reddish specular and sharper 
table.insert(spaceships,makespaceship({3,15,0},{1.4,0.7,0.7,32}))

Add sunlight and camera animation and such, and you will get a result similar as above.