Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!






ShaderMap 2 SDK Tutorial - Creating Maps

Posted by RSI3D, in ShaderMap 16 July 2012 · 1,308 views

Each map in ShaderMap 2 is a plugin. The plugin is a Windows DLL with the file extension .smp. To create a map plugin you will need to download the ShaderMap 2 SDK. http://shadermap.com/downloads/ As of this tutorial I am using version 2.0.5. You will also need Visual Studio 2008 - 2010.

Once you have the SDK downloaded and unzipped navigate to the folder 'map'. Inside you will see the VS solution file called map.sln. This solution contains 3 project files. Each project builds an example map. The 3 maps demonstrate how to build:

- source maps
- maps with no inputs
- maps with inputs

For the purpose of this tutorial I will be focusing on the third project. Right click on the project 'example_3' and set it as the default project. Open the source file 'example_3.cpp' so you can follow along.

Each map plugin uses the file 'map_plugin_core.cpp' which contains all API functions for interacting with ShaderMap 2. It is included at the top of each plugin file.

Each map plugin must implement 4 functions that are prototyped in the 'map_plugin_core.cpp' file. They are:

// Initialize plugin - Called when the plugin is loaded.
// This tells ShaderMap about the plugin and sets up the plugin property controls
BOOL on_initialize(void);

// Process plugin - called each time the map needs to be regenerated - the map_id is used to gain access to the map property values.
BOOL on_process(unsigned int map_id);

// Called just before the plugin is unloaded when ShaderMap is shutdown.
// If global resources are used this is the place to release them.
BOOL on_shutdown(void);

// Called when a project is loaded - It sends the version of the plugin with which the project was saved.
// The values are arranged in the order 0,1,2,...property index.
// If the order of the property controls have changed since the version parameter then modify the index array accordingly.
void on_arrange_load_data(unsigned int version, unsigned int index_count, unsigned int* index_array);


Here is the on_initialize function:
// Initialize plugin - This tells ShaderMap about the plugin and sets up the plugin property controls
// Is called when the plugin is loaded by ShaderMap
BOOL on_initialize(void)
{		
	// Tell ShaderMap we are starting initialize
	mp_begin_initialize();

		// Set version - useful for tracking property changes
		// from version to version. See on_arrange_load_data()
		mp_set_version(101);

		// Tell ShaderMap this map plugin is type MAP
		mp_set_map_type(MAP_PLUGIN_TYPE_MAP);

		// Set name and description
		mp_set_name(_T("Example 3 Map"));
		mp_set_description(_T("An example of normal map plugin with 1 input."));

		// Set the thumbnail filename. This image must be located in "APP_DIRECTORY\plugins\(x86|x64)\maps\thumbs"
		// See examples of thumbnails in that folder.
		// Thumbnail must be 128 x 128 pixels but thumb is much smaller at 71 x 71 and offset from top-left by 12 pixels
		mp_set_thumbnail(_T("example_3.png"));

		// Set default save format for the map
		mp_set_default_format(MAP_FORMAT_TGA_RGB_8);

		// Is a normal map
		mp_enable_normal_map(TRUE);
		
		// Add 1 input
		mp_add_input(_T("Normal Map"), _T("An image with normal vectors stored per pixel."));

		// Add properties controls																										Property Index
		// Add a coodinate system property with a default coordinate system set
		mp_add_property_coordsys(_T("Coord System"), MAP_COORDSYS_X_POS_RIGHT | MAP_COORDSYS_Y_POS_DOWN | MAP_COORDSYS_Z_POS_NEAR, 0);	// 0

	// Tell ShaderMap initialize was success - map is added
	mp_end_initialize();
	
	// Success
	return TRUE;
}


The first and last API calls are mp_begin_initialize(); and mp_end_initialize(); Everything else is between those two functions.

Here we setup the version integer, the map type (source or map), the name and description, the thumbnail filename, default file save format, if the map is a normal map or not (if so then will require a coordinate system to be set), map inputs with a description, and property controls associated with the map.

Here we have set up a map that is a normal map and takes a single normal map as input. It has one property control which is a coordinate system control. In essence this map allows the user to transform the normals of the input map to a new coordinate system. But you could make it do anything (randomize normals, etc).

The on_process function is shown below:

// Process plugin - called each time the map needs to be regenerated
BOOL on_process(unsigned int map_id)
{
	// Local data
	unsigned int			thread_limit, width, height, origin, tile_type, image_type, input_coord_system, user_coord_system, i, count_i;
	float					x, y, z;

	const unsigned char*	input_pixel_array_8;
	const unsigned short*	input_pixel_array_16;

	unsigned char*			map_pixel_d_array_8;
	unsigned short*			map_pixel_d_array_16;
	

	// Get suggested thread limit for plugin process - not used in this example
	thread_limit			= mp_get_map_thread_limit();

	// -----------------

	// Get size of input
	width				= (int)mp_get_input_width(map_id, 0);
	height				= (int)mp_get_input_height(map_id, 0);
	if(!width || !height)
	{	
		LOG_ERROR_MSG(map_id, _T("Invalid input size. Width or height is zero."));
		return FALSE;
	}
	// Get origin of input
	origin				= mp_get_input_origin(map_id, 0);

	// Get tile type of input
	tile_type			= mp_get_input_tile_type(map_id, 0);

	// Get image type of input
	image_type			= mp_get_input_image_type(map_id, 0);

	// As the input is a normal map - we must get its coordinate system
	input_coord_system	= mp_get_input_coordsys(map_id, 0);

	// -----------------

	// Tell ShaderMap the tile type of this map so children of this map inherit the tile type
	switch(tile_type)
	{
	case 0:
		mp_set_map_tile_type(map_id, MAP_TILE_NONE);
		break;
	case 1:
		mp_set_map_tile_type(map_id, MAP_TILE_X);
		break;
	case 2:
		mp_set_map_tile_type(map_id, MAP_TILE_Y);
		break;
	case 3:
		mp_set_map_tile_type(map_id, MAP_TILE_XY);
		break;
	}

	// -----------------

	// Allocate map pixels and get pointer to input pixel array
	// 8 bit
	if(image_type == MAP_IMAGE_TYPE_8)
	{
		// Allocate 8 bit map pixels
		map_pixel_d_array_8 = new (std::nothrow) unsigned char[width * height * 4];
		if(!map_pixel_d_array_8)
		{	
			LOG_ERROR_MSG(map_id, _T("Failed to allocate map pixels."));
			return FALSE;
		}

		// Get pixel array of input 0
		input_pixel_array_8 = (const unsigned char*)mp_get_input_pixel_array(map_id, 0);
	}
	// 16 bit
	else
	{
		// Allocate 16 bit map pixels
		map_pixel_d_array_16 = new (std::nothrow) unsigned short[width * height * 4];
		if(!map_pixel_d_array_16)
		{	
			LOG_ERROR_MSG(map_id, _T("Failed to allocate map pixels."));
			return FALSE;
		}

		// Get pixel array of input 0
		input_pixel_array_16 = (const unsigned short*)mp_get_input_pixel_array(map_id, 0);
	}

	// -----------------
	
	// Get property values
	user_coord_system	= mp_get_property_coordsys(map_id, 0);	

	// -----------------

	// Set the map coordsys as defined by the user
	mp_set_map_coordsys(map_id, user_coord_system);

	// Get number of items in the pixel arrays
	count_i				= width * height * 4;

	// 8 bit
	if(image_type == MAP_IMAGE_TYPE_8)
	{
		// For every pixel
		for(i=0; i<count_i; i+=4)
		{
			// Convert input pixel to normal
			x			= 2.0f * ((float)input_pixel_array_8[i] / 255.0f) - 1.0f;
			y			= 2.0f * ((float)input_pixel_array_8[i+1] / 255.0f) - 1.0f;
			z			= 2.0f * ((float)input_pixel_array_8[i+2] / 255.0f) - 1.0f;

			// Normalize vector
			normalize_vector(x, y, z);

			// Compare input coordsys with user coordsys and flip values as needed
			// Adjust X
			if((input_coord_system & MAP_COORDSYS_X_POS_RIGHT) != (user_coord_system & MAP_COORDSYS_X_POS_RIGHT))
			{	x		= -x;
			}
			// Adjust Y
			if((input_coord_system & MAP_COORDSYS_Y_POS_DOWN) != (user_coord_system & MAP_COORDSYS_Y_POS_DOWN))
			{	y		= -y;
			}
			// Adjust Z
			if((input_coord_system & MAP_COORDSYS_Z_POS_NEAR) != (user_coord_system & MAP_COORDSYS_Z_POS_NEAR))
			{	z		= -z;
			}

			// Store normal as color in the map pixel array
			map_pixel_d_array_8[i]		= (unsigned char)((x + 1.0f) / 2.0f * 255.0f);
			map_pixel_d_array_8[i+1]	= (unsigned char)((y + 1.0f) / 2.0f * 255.0f);
			map_pixel_d_array_8[i+2]	= (unsigned char)((z + 1.0f) / 2.0f * 255.0f);
			map_pixel_d_array_8[i+3]	= input_pixel_array_8[i+3];

			// Check for cancel
			if(mp_is_cancel_process())
			{	delete [] map_pixel_d_array_8;
				return FALSE;
			}
		}
	}
	// 16 bit
	else
	{
		// For every pixel
		for(i=0; i<count_i; i+=4)
		{
			// Convert input pixel to normal
			x			= 2.0f * ((float)input_pixel_array_16[i] / 65535.0f) - 1.0f;
			y			= 2.0f * ((float)input_pixel_array_16[i+1] / 65535.0f) - 1.0f;
			z			= 2.0f * ((float)input_pixel_array_16[i+2] / 65535.0f) - 1.0f;			

			// Normalize vector
			normalize_vector(x, y, z);

			// Compare user coordsys and flip values as needed
			// Adjust X
			if((input_coord_system & MAP_COORDSYS_X_POS_RIGHT) != (user_coord_system & MAP_COORDSYS_X_POS_RIGHT))
			{	x		= -x;
			}
			// Adjust Y
			if((input_coord_system & MAP_COORDSYS_Y_POS_DOWN) != (user_coord_system & MAP_COORDSYS_Y_POS_DOWN))
			{	y		= -y;
			}
			// Adjust Z
			if((input_coord_system & MAP_COORDSYS_Z_POS_NEAR) != (user_coord_system & MAP_COORDSYS_Z_POS_NEAR))
			{	z		= -z;
			}

			// Store normal as color in the map pixel array
			map_pixel_d_array_16[i]		= (unsigned short)((x + 1.0f) / 2.0f * 65535.0f);
			map_pixel_d_array_16[i+1]	= (unsigned short)((y + 1.0f) / 2.0f * 65535.0f);
			map_pixel_d_array_16[i+2]	= (unsigned short)((z + 1.0f) / 2.0f * 65535.0f);
			map_pixel_d_array_16[i+3]	= input_pixel_array_16[i+3];

			// Check for cancel
			if(mp_is_cancel_process())
			{	delete [] map_pixel_d_array_8;
				return FALSE;
			}
		}
	}

	// -----------------

	// Create the final map using the map pixels
	// 8 bit
	if(image_type == MAP_IMAGE_TYPE_8)
	{
		if(!mp_create_map(map_id, width, height, origin, image_type, map_pixel_d_array_8))
		{	
			LOG_ERROR_MSG(map_id, _T("Failed to create map with mp_create_map()."));
			delete [] map_pixel_d_array_8;
			return FALSE;
		}

		// Cleanup
		delete [] map_pixel_d_array_8;
		map_pixel_d_array_8 = 0;
	}
	// 16 bit
	else
	{
		if(!mp_create_map(map_id, width, height, origin, image_type, map_pixel_d_array_16))
		{	
			LOG_ERROR_MSG(map_id, _T("Failed to create map with mp_create_map()."));
			delete [] map_pixel_d_array_16;
			return FALSE;
		}

		// Cleanup
		delete [] map_pixel_d_array_16;
		map_pixel_d_array_16 = 0;
	}

	// Update map progress 100%
	mp_set_map_progress(map_id, 100);	

	// Success
	return TRUE;
}


The first part of the function uses API calls to get info about the input map and the property values, The next registers the tile type of the map. A pixel array is allocated based on 8 bit or 16 bit channels at the same size as the input map. The coordinate system is registered as the one the user has defined in the coordinate system property control. Depending on the image type the pixels (normals) are copied into the new pixel array and set to the new coordinate system.

At the end of the function the pixels are sent to ShaderMap using the mp_create_map() function. Which takes the map_id, size, and array of pixels in RGBA format.

To install this plugin, add the binary *.SMP file to the ShaderMap plugins directory: "SM2_DIRECTORY\plugins\(x86 or x64)\maps\"

and install the plugin thumbnail to the directory: "SM2_DIRECTORY\plugins\(x86 or x64)\maps\thumbs\"

Now the map will be available from the Advanced Map Setup page inside ShaderMap 2.




Twitter

Posted Image

Google GameDev.Net Search Bar

Posted Image

Recent Comments

PARTNERS