Material Definition Language API nvidia_logo_transpbg.gif Up
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
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 methods mi::neuraylib::IMaterial_definition::create_material_instance() and mi::neuraylib::IFunction_definition::create_function_call() are used to instantiate a given material or function definition, respectively.

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::IMaterial_instance::set_argument() or 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".

To do so, one has to prepare a structure of type "Variant_data" with all the required information: the name of the definition, the name of the prototype it is based on, the new defaults, and (optionally) an annotation for the variant. All such structures are combined in an array (one for each variant to be created, just one in this example). The annotation is created via mi::neuraylib::IMdl_factory::create_variants().

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 2020 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";
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));
mdl_factory->create_execution_context());
// 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".
material_definition_name));
// 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_material_instance( 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)".
function_definition_name));
// Since not all parameters of this function definition have defaults we have to provide
// explicitly initial arguments to create an instance of the definition.
expression_factory->create_expression_list());
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::IMaterial_instance>( material_instance_name));
check_success( material_instance.is_valid_interface());
// Get the old argument for the "tint" parameter and clone it.
material_instance->get_arguments());
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()));
new_argument->get_interface<mi::neuraylib::IExpression_constant>());
new_argument_constant->get_value<mi::neuraylib::IValue_color>());
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";
break;
}
{
mi::Float32 float_value = 0.0f;
annotations.get_annotation_param_value<mi::Float32>( a, p, float_value);
std::cout << float_value << "\n";
break;
}
default:
{
// 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";
break;
}
}
}
}
// 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 << "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::IMaterial_instance>( material_instance_name));
material_instance->get_arguments());
expression_factory->clone( arguments.get()));
// Create an ::anno::description annotation.
value_factory->create_string(
"a variant of ::nvidia::sdk_examples::tutorials::example_material"
" with different defaults"));
expression_factory->create_constant( anno_arg_value.get()));
expression_factory->create_expression_list());
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()));
expression_factory->create_expression_list());
range_args->add_expression( "min", range_min_expression.get());
range_args->add_expression( "max", range_max_expression.get());
expression_factory->create_annotation(
"::anno::hard_range(float,float)", range_args.get()));
// Add annotations to a annotation block
expression_factory->create_annotation_block());
anno_block->add_annotation( anno.get());
anno_block->add_annotation( range_anno.get());
// Set up variant data: an array with a single element of type Variant_data for variant
// name, prototype name, new defaults, and the annotation created above.
transaction->create<mi::IArray>( "Variant_data[1]"));
variant_data->get_value<mi::IStructure>( static_cast<mi::Size>( 0)));
variant->get_value<mi::IString>( "variant_name"));
variant_name->set_c_str( "green_example_material") ;
variant->get_value<mi::IString>( "prototype_name"));
prototype_name->set_c_str( "mdl::nvidia::sdk_examples::tutorials::example_material");
check_success( variant->set_value( "defaults", defaults.get()) == 0);
check_success( variant->set_value( "annotations", anno_block.get()) == 0);
// print the annotations just to illustrate the convince helper
print_annotations( type_factory.get(), anno_block.get());
// Create the variant.
check_success( mdl_factory->create_variants(
transaction, "::variants", variant_data.get()) == 0);
// Instantiate the material definition of the variant.
"mdl::variants::green_example_material"));
mi::Sint32 result = 0;
material_instance = material_definition->create_material_instance( 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());
}
transaction->commit();
}
// 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.");
exit_success();
}
// Convert command line arguments to UTF8 on Windows
COMMANDLINE_TO_UTF8
[Previous] [Up] [Next]