MDL SDK API nvidia_logo_transpbg.gif Up
Example for Instantiation of MDL Definitions
[Previous] [Up] [Next]

This example creates material instances and function calls by instantiating the corresponding material and function definitions. It also shows how to change arguments of material instances and function calls.

New Topics

  • Instantiating material and function definitions
  • Changing arguments
  • Creating material and function variants

Detailed Description

Instantiating material and function definitions

The method instantiate_definitions() shows how to instantiate a material or function definition from a previously loaded module. The method mi::neuraylib::IFunction_definition::create_function_call() is used to instantiate a given material or function definition.

There are two important cases to distinguish: all parameters have defaults, or some parameters have no defaults. The first part of the method deals with a material definition where all parameters have defaults. Therefore, when creating the material instance, it is not necessary to provide any arguments since the defaults are used automatically. The second part of the method deals with a function definition without defaults for its parameters. It is therefore mandatory to provide the initial arguments.

Changing arguments

The method change_arguments() shows how to change an argument of a material instance or a function call. Essentially, you have to call mi::neuraylib::IFunction_call::set_argument() with the new argument. The method illustrates two ways for obtaining the new argument.

The first way is to create to obtain the old value, to clone it (because it is immutable), to modify the value, and to pass it to the material instance or function call. The second way is to use mi::neuraylib::Argument_editor::set_value() which simplifies these steps.

Creating material and function variants

A variant is a material or function definition that is based on a prototype definition, but has different defaults. In this example we create an MDL module "::variants" with a single new material definition: a variant of "::example::diffuse_material", but with a green default for "tint".

Variants are created with the help of the module builder. Use mi::neuraylib::IMdl_factory::create_module_builder() to create an instance of the module builder, passing the module name and the minimal and maximal desired MDL version of the new module.

Use mi::neuraylib::IMdl_module_builder::add_variant() to create new variants. This requires the name of the new variant, and the name of the prototype it is based on. Optionally, you can specify other properties, like new defaults, annotations, return annotations, and the flag for the 'export' key.

A dump of the instantiated definition as well as the exported module show that the new material definition really is a variant of the prototype but with different defaults.

Example Source

Source Code Location: examples/mdl_sdk/instantiation/example_instantiation.cpp

* Copyright 2024 NVIDIA Corporation. All rights reserved.
// examples/mdl_sdk/instantiation/example_instantiation.cpp
// Instantiates a material and a function definition and changes argument values.
#include <iostream>
#include <string>
#include "example_shared.h"
const char* material_definition_name
= "mdl::nvidia::sdk_examples::tutorials::example_material(color,float)";
const char* material_instance_name = "instance of example_material";
const char* function_definition_name
= "mdl::nvidia::sdk_examples::tutorials::example_function(color,float)";
const char* function_call_name = "call of example_function";
// Utility function to dump the arguments of a material instance or function call.
template <class T>
void dump_instance(
mi::neuraylib::IExpression_factory* expression_factory, const T* material, std::ostream& s)
mi::Size count = material->get_parameter_count();
mi::base::Handle<const mi::neuraylib::IExpression_list> arguments( material->get_arguments());
for( mi::Size index = 0; index < count; index++) {
arguments->get_expression( index));
std::string name = material->get_parameter_name( index);
expression_factory->dump( argument.get(), name.c_str(), 1));
s << " argument " << argument_text->get_c_str() << std::endl;
s << std::endl;
// Instantiates a material definition and a function definition.
void instantiate_definitions(
mdl_factory->create_value_factory( transaction));
mdl_factory->create_expression_factory( transaction));
// Load the module "tutorials" and access it from the DB.
check_success( mdl_impexp_api->load_module(
transaction, "::nvidia::sdk_examples::tutorials", context.get()) >= 0);
print_messages( context.get());
transaction->access<mi::neuraylib::IModule>( "mdl::nvidia::sdk_examples::tutorials"));
check_success( module.is_valid_interface());
// Instantiation of a material definition
// Access the material definition "example_material".
// Since all parameter of this material definition have defaults we can instantiate the
// definition without the need to explicitly provide initial arguments.
mi::Sint32 result = 0;
material_definition->create_function_call( 0, &result));
check_success( result == 0);
std::cout << "Dumping material instance \"" << material_instance_name << "\":" << std::endl;
dump_instance( expression_factory.get(), material_instance.get(), std::cout);
// We are going to change the arguments later, so store the instance for now in the DB.
transaction->store( material_instance.get(), material_instance_name);
// Instantiation of a function definition
// Access the function definition "example_function(color,float)".
// Since not all parameters of this function definition have defaults we have to provide
// explicitly initial arguments to create an instance of the definition.
value_factory->create_color( 1.0f, 0.0f, 0.0f));
expression_factory->create_constant( tint_value.get()));
arguments->add_expression( "tint", tint_expr.get());
value_factory->create_float( 2.0f));
expression_factory->create_constant( distance_value.get()));
arguments->add_expression( "distance", distance_expr.get());
// Instantiate the function definition using "arguments" as initial arguments.
mi::Sint32 result = 0;
function_definition->create_function_call( arguments.get(), &result));
check_success( result == 0);
std::cout << "Dumping function call \"" << function_call_name << "\":" << std::endl;
dump_instance( expression_factory.get(), function_call.get(), std::cout);
// We are going to change the arguments later, so store the call for now in the DB.
transaction->store( function_call.get(), function_call_name);
// Changes the arguments of a previously create material instance or function call.
void change_arguments( mi::neuraylib::INeuray* neuray, mi::neuraylib::ITransaction* transaction)
mdl_factory->create_value_factory( transaction));
mdl_factory->create_expression_factory( transaction));
// Changing arguments: cloning the old value and modifying the clone
// Edit the instance of the material definition "example_material".
transaction->edit<mi::neuraylib::IFunction_call>( material_instance_name));
check_success( material_instance.is_valid_interface());
// Get the old argument for the "tint" parameter and clone it.
arguments->get_expression( "tint"));
// Change the value of the clone and set it as new argument. Here we use prior knowledge
// that the type of the parameter is a color and the expression is a constant.
expression_factory->clone( argument.get()));
set_value( new_argument_value.get(), mi::math::Color( 0.0f, 1.0f, 0.0f));
check_success( material_instance->set_argument( "tint", new_argument.get()) == 0);
std::cout << "Dumping modified material instance \"" << material_instance_name << "\":"
<< std::endl;
dump_instance( expression_factory.get(), material_instance.get(), std::cout);
// Changing arguments: using the argument editor
// Edit the instance of the function definition "volume_coefficient()". Here we use
// prior knowledge that the type of the parameter is a color.
transaction, function_call_name, mdl_factory.get());
mi::math::Color blue( 0.0f, 0.0f, 1.0f);
check_success( argument_editor.set_value( "tint", blue) == 0);
transaction->edit<mi::neuraylib::IFunction_call>( function_call_name));
check_success( function_call.is_valid_interface());
std::cout << "Dumping modified function call \"" << function_call_name << "\":"
<< std::endl;
dump_instance( expression_factory.get(), function_call.get(), std::cout);
// Iterates over an annotation block and prints annotations and their parameters.
void print_annotations(
const mi::neuraylib::IType_factory* type_factory,
mi::neuraylib::Annotation_wrapper annotations( anno_block);
std::cout << "There are "
<< annotations.get_annotation_count() << " annotation(s).\n";
for( mi::Size a = 0; a < annotations.get_annotation_count(); ++a)
std::string name = anno_def->get_mdl_simple_name();
std::cout << " \"" << name << "\" with "
<< annotations.get_annotation_param_count( a) << " parameter(s):\n";
for( mi::Size p = 0; p < annotations.get_annotation_param_count( a); ++p)
annotations.get_annotation_param_type( a, p));
mi::base::Handle<const mi::IString> type_text( type_factory->dump( type_handle.get()));
std::cout << " \"" << annotations.get_annotation_param_name( a, p)
<< "\" of type \""
<< type_text->get_c_str() << "\" = ";
switch( type_handle->get_kind())
const char* string_value = nullptr;
annotations.get_annotation_param_value<const char*>( a, p, string_value);
std::cout << "\"" << string_value << "\"\n";
mi::Float32 float_value = 0.0f;
annotations.get_annotation_param_value<mi::Float32>( a, p, float_value);
std::cout << float_value << "\n";
// type not handled in this example
std::cout << "Address: " << mi::base::Handle<const mi::neuraylib::IValue>(
annotations.get_annotation_param_value( a, p)).get() << "\n";
// some other convenient helpers
std::cout << "\n";
std::cout << "Index of \"hard_range\": " << annotations.get_annotation_index(
"::anno::hard_range(float,float)") << "\n";
std::cout << "Index of \"foo\": " << static_cast<mi::Sint32>( annotations.get_annotation_index(
"::anno::foo(int)")) << " (which is not present)\n";
const char* descValue = nullptr;
mi::Sint32 res = annotations.get_annotation_param_value_by_name<const char*>(
"::anno::description(string)", 0, descValue);
std::cout << R"(Value of "description": ")" << ( res == 0 ? descValue : "nullptr") << "\"\n";
mi::Sint32 fooValue = 0;
res = annotations.get_annotation_param_value_by_name<mi::Sint32>(
"::anno::foo(int)", 0, fooValue);
if ( res != 0)
std::cout << "Value of \"foo\" not found (annotation is not present)\n";
std::cout << std::endl;
// Creates a variant of the example material with different defaults.
void create_variant( mi::neuraylib::INeuray* neuray, mi::neuraylib::ITransaction* transaction)
mdl_factory->create_type_factory( transaction));
mdl_factory->create_value_factory( transaction));
mdl_factory->create_expression_factory( transaction));
// Prepare new defaults as clone of the current arguments of the material instance.
transaction->access<mi::neuraylib::IFunction_call>( material_instance_name));
expression_factory->clone( arguments.get()));
// Create an ::anno::description annotation.
"a variant of ::nvidia::sdk_examples::tutorials::example_material"
" with different defaults"));
expression_factory->create_constant( anno_arg_value.get()));
anno_args->add_expression( "description", anno_arg_expression.get());
expression_factory->create_annotation( "::anno::description(string)", anno_args.get()));
// Create an ::anno::hard_range annotation.
mi::base::Handle<mi::neuraylib::IValue> range_min_value( value_factory->create_float( 1.0));
mi::base::Handle<mi::neuraylib::IValue> range_max_value( value_factory->create_float( 1024.0));
expression_factory->create_constant( range_min_value.get()));
expression_factory->create_constant( range_max_value.get()));
range_args->add_expression( "min", range_min_expression.get());
range_args->add_expression( "max", range_max_expression.get());
"::anno::hard_range(float,float)", range_args.get()));
// Add annotations to a annotation block.
anno_block->add_annotation( anno.get());
anno_block->add_annotation( range_anno.get());
// Print the annotations just to illustrate the convince helper.
print_annotations( type_factory.get(), anno_block.get());
// Create the module bulder.
check_success( module_builder);
// Create the variant.
check_success( module_builder->add_variant(
/*return_annotations*/ nullptr,
/*is_exported*/ true,
/*is_declarative*/ true,
context.get()) == 0);
// Instantiate the material definition of the variant.
mi::Sint32 result = 0;
material_instance = material_definition->create_function_call( 0, &result);
check_success( result == 0);
std::cout << "Dumping material instance with defaults of material definition "
"\"mdl::variants::green_example_material\":" << std::endl;
dump_instance( expression_factory.get(), material_instance.get(), std::cout);
// Export the variant.
check_success( mdl_impexp_api->export_module( transaction, "mdl::variants", "variants.mdl") == 0);
int MAIN_UTF8( int /*argc*/, char* /*argv*/[])
// Access the MDL SDK
mi::base::Handle<mi::neuraylib::INeuray> neuray(mi::examples::mdl::load_and_get_ineuray());
if (!neuray.is_valid_interface())
exit_failure("Failed to load the SDK.");
// Configure the MDL SDK
if (!mi::examples::mdl::configure(neuray.get()))
exit_failure("Failed to initialize the SDK.");
// Start the MDL SDK
mi::Sint32 ret = neuray->start();
if (ret != 0)
exit_failure("Failed to initialize the SDK. Result code: %d", ret);
mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
// Instantiate a material and function definition
instantiate_definitions( neuray.get(), transaction.get());
// Change the arguments of the instantiated definitions
change_arguments( neuray.get(), transaction.get());
// Create a variant of the diffuse material with different defaults.
create_variant( neuray.get(), transaction.get());
// Shut down the MDL SDK
if (neuray->shutdown() != 0)
exit_failure("Failed to shutdown the SDK.");
// Unload the MDL SDK
neuray = nullptr;
if (!mi::examples::mdl::unload())
exit_failure("Failed to unload the SDK.");
// Convert command line arguments to UTF8 on Windows
Handle class template for interfaces, automatizing the lifetime control via reference counting.
Definition: handle.h:113
Standard RGBA color class with floating point elements and operations.
Definition: color.h:81
A wrapper around the interfaces for MDL annotations.
Definition: annotation_wrapper.h:37
A wrapper around the interface for MDL material instances and function calls.
Definition: argument_editor.h:57
An annotation block is an array of annotations.
Definition: iexpression.h:575
virtual Sint32 add_annotation(IAnnotation *annotation)=0
Adds an annotation at the end of the annotation block.
virtual const IAnnotation * get_annotation(Size index) const =0
Returns the annotation for index, or NULL if index is out of bounds.
This interface is used to interact with the distributed database.
Definition: idatabase.h:289
A constant expression.
Definition: iexpression.h:96
The interface for creating expressions.
Definition: iexpression.h:650
virtual const IString * dump(const IExpression *expr, const char *name, Size depth=0) const =0
Returns a textual representation of an expression.
virtual IExpression_list * create_expression_list() const =0
Creates a new expression list.
virtual IExpression * clone(const IExpression *expr) const =0
Clones the given expression.
virtual IAnnotation * create_annotation(const char *name, const IExpression_list *arguments) const =0
Creates a new annotation.
virtual IAnnotation_block * create_annotation_block() const =0
Creates a new annotation block.
virtual IExpression_constant * create_constant(IValue *value) const =0
Creates a constant (mutable).
This interface represents a function call.
Definition: ifunction_call.h:52
This interface represents a function definition.
Definition: ifunction_definition.h:44
Factory for various MDL interfaces and functions.
Definition: imdl_factory.h:53
API component for MDL related import and export operations.
Definition: imdl_impexp_api.h:43
This interface represents an MDL module.
Definition: imodule.h:634
This is an object representing the MDL SDK library.
Definition: ineuray.h:44
virtual Sint32 shutdown(bool blocking=true)=0
Shuts down the library.
virtual base::IInterface * get_api_component(const base::Uuid &uuid) const =0
Returns an API component from the MDL SDK API.
virtual Sint32 start(bool blocking=true)=0
Starts the operation of the MDL SDK library.
A transaction provides a consistent view on the database.
Definition: itransaction.h:84
virtual const base::IInterface * access(const char *name)=0
Retrieves an element from the database.
virtual base::IInterface * edit(const char *name)=0
Retrieves an element from the database and returns it ready for editing.
virtual Sint32 commit()=0
Commits the transaction.
virtual Sint32 store(base::IInterface *db_element, const char *name, Uint8 privacy=LOCAL_SCOPE)=0
Stores the element db_element in the database under the name name and with the privacy level privacy.
The interface for creating types.
Definition: itype.h:716
virtual const IString * dump(const IStruct_category *struct_category, Size depth=0) const =0
Returns a textual representation of a struct category.
The string type. See mi::neuraylib::IType_string.
Definition: itype.h:168
The float type. See mi::neuraylib::IType_float.
Definition: itype.h:164
A value of type color.
Definition: ivalue.h:374
Interface * get() const
Access to the interface. Returns 0 for an invalid interface.
Definition: handle.h:294
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
float Float32
32-bit float.
Definition: types.h:51
signed int Sint32
32-bit signed integer.
Definition: types.h:46
mi::Sint32 set_value(mi::neuraylib::IValue *value, const T &v)
Simplifies setting the value of mi::neuraylib::IValue from the corresponding classes from the base an...
Definition: ivalue.h:925
MDL version 1.0.
Definition: iexpression.h:29
Latest MDL version.
Definition: iexpression.h:40
[Previous] [Up] [Next]