Krakatoa SR C++ API

We are currently working on a C++ API for Krakatoa SR. This API would be a DLL/shared library that any program could link to. The Python scene description format will still be fully supported.

The advantage of the API would be that technical users could program could directly stream particles of any source and import matte geometry without having to write to intermediate file formats. Another advantage is with the API would be the ability to integrate Krakatoa SR directly within any content creation program (such as Maya).

This new API is very exciting for us, as it will allow users to tap into the full potential of Krakatoa without the need for clunky exporters and intermediate file formats.

The first version of the API will allow technical users to:

  • Programmaticly create scene descriptions: Place standard cameras and lights, and modify render settings. Camera and light models are currently pre-defined by Krakatoa.
  • Programmaticly create particles: Allow for particles to be fed into the renderer directly without the need for a PRT file.
  • Programmaticly create matte triangle meshes: Allow for matte meshes to be inputted directly without the need for a OBJ mesh file.

This may be expanded afterwards.

As always, input and feedback from our beta testers is very valuable. Is there anything else that you believe would be useful for this version or subsequent versions of the API?

-Conrad

Same forum and such?

Incredible, just what I asked for few days ago;)

cheers

tyler

great news people, cant wait for it

cheers

Will we be able to define regions in space to rasterize after the “rendering” is done? Meaning, after Krakatoa finishes the lighting passes and all the data is in the particles in memory nicely sorted to our camera, can we set either cameraspace or worldspace regions to rasterize? For doing ROI or scanline or bucket rendering, the cameraspace rasterizing control would be super useful. Add in camera depth as a parameter, and we could do on-demand depth sampling for deep compositing.

Generally, I would like to see the full feature list that Krakatoa MX supports currently also in this API. Like Magma Flow etc…

This seems super-interesting indeed !
I am looking forward to it !

It is great to have C++ API for Krakatoa SR !

There is function to save resulting image to the file.

void set_output_image_file( const char* filename ); 

Will there be a function to access this resulting in the memory ?
Something like this ?

void set_output_image_memory( image_stream &img ); 

regards,
Remo

Hi,

Is there a HelloWorld type example to get us off to a good start ?

Regards

That’s a great idea. We will post a simple example soon.

Thanks,
Ian

Indeed, a small example would help a lot.
An api is great. Thanks a lot.

I’m trying to create a simple default rendering. If I understand the situation correctly then I cannot fill a mesh yet, because meshes are
not implemented in the api yet. So to get a particle rendering I have to create a particle stream with a particle stream with my own particle stream interface.
From the header file I used the example interface with a small change, it seems that the method called get_particle() should be called get_next_particle() is that correct?

So this is my interface:

class my_stream : public krakatoasr::particle_stream_interface {
 	krakatoasr::channel_data m_position;
 	krakatoasr::channel_data m_velocity;
 	krakatoasr::channel_data m_density;
 	int m_count;
public:
 	my_stream() {
 		m_position = append_channel( "Position", krakatoasr::DATA_TYPE_FLOAT32, 3 );
 		m_velocity = append_channel( "Velocity",krakatoasr::DATA_TYPE_FLOAT32, 3 );
 		m_density = append_channel( "Density", krakatoasr::DATA_TYPE_FLOAT32, 1 );
 		m_count = 0;
 	}
 	krakatoasr::INT64 particle_count() const {
 		return 100;
 	}
 	bool get_next_particle( void* particleData ) {
 		//set these values to your own custom particle values
 		float myPosition[3] = { rand() * 10.0f / RAND_MAX, rand() * 10.0f / RAND_MAX, rand() * 10.0f / RAND_MAX };
 		float myVelocity[3] = { 0.0f, 0.0f, 0.0f };
 		float myDensity = 1.0f;
 		set_channel_value( m_position, particleData, myPosition );
 		set_channel_value( m_velocity, particleData, myVelocity );
 		set_channel_value( m_density, particleData, &myDensity );
 		++m_count;
 		return ( m_count <= 100 );
 	}

 	void close() {}
};

And in my maya plugin I try this sequence in a subclass of krakatoa_renderer:

	MString outputFile("C:/daten/3dprojects/krakatoa/images/kraka_test.exr");
	set_output_image_file( outputFile.asChar() );

	set_error_on_missing_license(false);

	set_background_color(1.0f, 0.0f, 0.0f);

	krakatoasr::animated_transform tm;
	set_camera_tm( tm );
	krakatoasr::camera_type_t ct = krakatoasr::CAMERA_PERSPECTIVE;
	set_camera_type( ct );
	set_camera_perspective_fov( 35.0f );
	set_camera_clipping( 0.01f, 1000.0f );

	//render output
	set_render_resolution( 640, 480 );
	set_pixel_aspect_ratio( 1.0f );

	krakatoasr::particle_stream ps;
	my_stream stream_if;
	ps.create_from_particle_stream_interface(&stream_if);

	add_particle_stream( ps );
	
	render();

As I expected, as soon as it tries to render, maya crashes :slight_smile:
Is there anything obviously wrong what can explain the crash?

If I use the plugin, I always get a FlexLM requester. I think it would be helpful if we could avoid the flexlm lookup and produce watermarked images directly during development. Or is this a problem with my system settings?

haggi

Thanks for taking a look at the API so soon.

First:

The best I can give you so far is to implement the ‘krakatoasr::frame_buffer_interface’, pass it into the method ‘set_frame_buffer_update’ on the krakatoa renderer. ‘set_frame_buffer’ will be called once per rendering pass, so you can cache that data as it comes in. Maybe something like this:

class my_frame_buffer_interface: public krakatoasr::frame_buffer_interface {
	std::vector<krakatoasr::frame_buffer_pixel_data> m_pixels;
public:
	virtual void set_frame_buffer( int width, int height, const krakatoasr::frame_buffer_pixel_data* data ) {
		m_pixels.resize(width * height);
		for (size_t i = 0; i < m_pixels.size(); ++i) {
			m_pixels[i] = data[i];
		}
	}
};

Obviously replacing the std::vector with your own preferred method of storage. Make sure you assign it to the renderer by calling ‘set_frame_buffer_update’ on the krakatoa_renderer instance before calling ‘render’.

Second, haggi, the problem with your code is the line:

ps.create_from_particle_stream_interface(&stream_if);

if you look at api.hpp, ‘create_from_particle_stream_interface’ is actually a static method of ‘particle_stream’ (that C++ lets you call class static methods on instances is a pet peeve of mine anyways; I was actually caught by the same issue when I was first using the API too!); the correct usage is:

ps = krakatoasr::particle_stream::create_from_particle_stream_interface(&stream_if);

as create_from_particle_stream_interface actually returns an instance of krakatoasr::particle_stream. Perhaps it is a little counter-intuitive to how the rest of the API works, maybe your perceived usage might be a better fit. This also brings to my attention that the renderer is a little unforgiving about when you pass in a non-initialized particle_stream object, we’ll probably look at changing that too, but I’ll have to discuss it with Conrad first.

Lastly, I’ve gone ahead and written a more in-depth example use of the API, that can be compiled as a standalone command-line application. I hope you don’t mind but I took the liberty of incorporating your stream interface in our example haggi. It really is a good, concise example of how to create a custom stream; originally, I was going to just have it load a .prt file from disk, but I think this might be more useful to people hoping to start using the API for more complex tasks. I’ve extended it to allow a variable number of particles, and also added a color channel as well.

[code]/**

  • This is a simple example of how to use the KrakatoaSR api to render a simple scene from a .prt file.
  • I believe this is close to the minimum that must be specified for a successful render.
  • Basically, the steps are:
    • set a shader
    • set the camera transform and settings
    • set a light
    • set a particle_stream
    • render
  • The steps before the render don’t really have to be in any specific order
    */

#include <api.hpp>

#include
#include
#include

/**

  • This is a very simple example of how to use the frame buffer interface

  • You could use this to access the image data of the render after it has completed.
    */
    class my_frame_buffer_interface: public krakatoasr::frame_buffer_interface {
    public:
    std::vectorkrakatoasr::frame_buffer_pixel_data m_pixels;

    virtual void set_frame_buffer( int width, int height, const krakatoasr::frame_buffer_pixel_data* data ) {
    m_pixels.resize(width * height);

     for (size_t i = 0; i < m_pixels.size(); ++i) {
     	m_pixels[i] = data[i];
     }
    

    }
    };

/**

  • This example stream interface was written by forum user ‘haggi’.

  • It shows how to use the interface to generate a random particle cloud.
    /
    class my_stream : public krakatoasr::particle_stream_interface {
    krakatoasr::channel_data m_position;
    krakatoasr::channel_data m_velocity;
    krakatoasr::channel_data m_density;
    krakatoasr::channel_data m_color;
    krakatoasr::INT64 m_currentParticle;
    krakatoasr::INT64 m_particleCount;
    public:
    my_stream(krakatoasr::INT64 particleCount = 100) {
    // the channel names used have special meaning to krakatoa. They are case-sensitive, and generally
    // must have the correct data type (i.e. some kind of float) and arity.
    m_position = append_channel( “Position”, krakatoasr::DATA_TYPE_FLOAT32, 3 );
    m_velocity = append_channel( “Velocity”,krakatoasr::DATA_TYPE_FLOAT32, 3 );
    m_density = append_channel( “Density”, krakatoasr::DATA_TYPE_FLOAT32, 1 );
    m_color = append_channel( “Color”, krakatoasr::DATA_TYPE_FLOAT32, 3);
    m_currentParticle = 0;
    m_particleCount = particleCount;
    }
    krakatoasr::INT64 particle_count() const {
    return m_particleCount;
    }
    bool get_next_particle( void
    particleData ) {
    // check if we have any more particles to emit
    if ( m_currentParticle < m_particleCount )
    {
    float myPosition[3] = { rand() * 10.0f / RAND_MAX, rand() * 10.0f / RAND_MAX, rand() * 10.0f / RAND_MAX };
    float myVelocity[3] = { 0.0f, 0.0f, 0.0f };
    float myColor[3] = { float(rand()) / float(RAND_MAX), float(rand()) / float(RAND_MAX), float(rand()) / float(RAND_MAX) };
    float myDensity = 1.0f;
    set_channel_value( m_position, particleData, myPosition );
    set_channel_value( m_velocity, particleData, myVelocity );
    set_channel_value( m_color, particleData, myColor );
    set_channel_value( m_density, particleData, &myDensity );
    ++m_currentParticle;
    return true;
    }

     return false;
    

    }

    void close() {}
    };

int main(const int argc, const char** argv)
{
if (argc != 2)
{
std::cout << “Usage: " << argv[0] << " <outputFileName.exr>” << std::endl;
return EXIT_FAILURE;
}

const std::string outputFilename = argv[1];

krakatoasr::krakatoa_renderer krakRenderer;

// produce a watermark if no license is found
krakRenderer.set_error_on_missing_license(false);

// these are set to reasonable defaults, but they can be modified
krakRenderer.set_density_per_particle(6.0f);
krakRenderer.set_density_exponent(-1.0f);

// first thing we need to do is set a shader
// we'll just use a simple isotropic shader, the other shaders tend to require more complex parameters to set-up
krakatoasr::shader_isotropic isotropicShader;

// you must set the shader, krakatoa SR assumes no default shader
krakRenderer.set_shader(&isotropicShader);

// the background is by default black, however I like to set it to a light blue, since then particles not receiving light will still show up
krakRenderer.set_background_color(0.47805f, 0.690593f, 0.903227f);

// the 'animated_transform' is just a collection of transformation matrices sampled over time.
// If you want to apply motion blur to your shot, you can add more than one transformation, and key it to different times around the frame.
// (ie. taking samples relative to the current frame at -0.4, -0.2, 0.0, +0.2, +0.4)
// it'll then be able to interpolate position given a shutter time
krakatoasr::animated_transform cameraXForm;

// set up the camera perspective
// Note that the camera transform should be the actual world-space location of the camera object, not its inverse
// Krakatoa SR will perform the inverting as neccessary.
// Normally you'd read this off of a camera object at render time or something
float cameraMatrix[16] = 
{
	0.707107f, 0.0f, -0.707107f, 0.0f,
	-0.408248f, 0.816497f, -0.408248f, 0.0f,
	0.57735f, 0.57735f, 0.57735f, 0.0f,
	10.0f, 12.0f, 10.0f, 1.0f,
};

// if we aren't doing motion blur, just specify the shutter time as 0
// this is also what the float* constructor does implicitly
cameraXForm.add_transform( cameraMatrix, 0.0f );

// if we were doing motion blur, you could add the other transforms at the desired shutter times as
// additional calls to 'add_transform'
// the following would show a rotation of the camera by 10 degrees between the previous frame and the next frame
/*
float cameraMatrixPreviousFrame[16] = 
{
	0.745106f, 0.0f, -0.666946f, 0.0f,
	-0.37111f, 0.8165f, -0.442272f, 0.0f,
	0.524836f, 0.57735f, 0.625476, 0.0f,
	10.0f, 12.0f, 10.0f, 1.0f,
};

cameraXForm.add_transform( cameraMatrixPreviousFrame, -1.0f );

float cameraMatrixNextFrame[16] = 
{
	0.666946f, 0.0f, -0.745106f, 0.0f,
	-0.442272f, 0.8165f, -0.37111f, 0.0f,
	0.625476f, 0.57734f5, 0.524836f, 0.0f,
	10.0f, 12.0f, 10.0f, 1.0f,
};

cameraXForm.add_transform( cameraMatrixNextFrame, 1.0f );

// ...

// you can keep adding more transforms at more shutter times in order to give a more exact sense of the camera's motion over the frame
// the renderer will linearly interpolate between the two closest specified shutter times when rendering that motion blur segment

krakRenderer.enable_motion_blur(true);

// we'll use a standard (180 degree) shutter (I think...), taking 10 motion blur samples in total, and choose not to use jittered motion blur
krakRenderer.set_motion_blur( -0.25f, 0.25f, 10, false );

*/

// once we're done, you have to set the camera transform on the render
krakRenderer.set_camera_tm( cameraXForm );

// set up the other camera parameters
krakRenderer.set_camera_type( krakatoasr::CAMERA_PERSPECTIVE );
krakRenderer.set_camera_clipping( 0.01f, 10000.0f );
krakRenderer.set_camera_perspective_fov(60.0f);

// to keep things simple, we'll avoid motion blur
krakRenderer.enable_motion_blur(false);

// we'll add a simple light to the scene
krakatoasr::point_light pointLight;

// the defaults for the light should be sufficient, but they can obviously be tweaked to one's liking

// to help with debugging, you can optionally name the scene lights, this name will be used by the progress logger
pointLight.set_name("the_light_that_I_created");

// much like the camera, we need to specify its transform
krakatoasr::animated_transform lightXForm;

// we'll just set the light a little off to the side
float lightMatrix[16] = 
{
	1.0f, 0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	0.0f, 0.0f, 1.0f, 0.0f,
	50.0f, 0.0f, 0.0f, 1.0f,
};

lightXForm.add_transform(lightMatrix, 0.0f);

/*
// We can also animate the transforms of the lights for use with motion blur renders

// for example, supposing the light is moving +2 units on the x axis between the previous frame and the next frame
float lightMatrixPreviousFrame[16] = 
{
	1.0f, 0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	0.0f, 0.0f, 1.0f, 0.0f,
	49.0f, 0.0f, 0.0f, 1.0f,
};

lightXForm.add_transform(lightMatrixPreviousFrame, -1.0f);

float lightMatrixNextFrame[16] = 
{
	1.0f, 0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	0.0f, 0.0f, 1.0f, 0.0f,
	51.0f, 0.0f, 0.0f, 1.0f,
};

lightXForm.add_transform(lightMatrixPreviousFrame, 1.0f);

*/

// note that once you call any of the 'add_*' or 'set_*' methods, the data is all copied internally
// into the renderer instance, so you don't need to worry about keeping that data around afterwards
krakRenderer.add_light(&pointLight, lightXForm);

// it is also possible to specify custom particle streams using the 'particle_stream_interface'
// for now, we'll just grab the particles from a prt file

my_stream streamInterfaceObject( 100 );

krakatoasr::particle_stream particleStream = krakatoasr::particle_stream::create_from_particle_stream_interface(&streamInterfaceObject);

krakatoasr::animated_transform particleXForm;

// by default animated transform should just return the identity matrix anyways, but being explicit never hurts
float particleMatrix[16] = 
{
	1.0f, 0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	0.0f, 0.0f, 1.0f, 0.0f,
	0.0f, 0.0f, 0.0f, 1.0f,
};

particleXForm.add_transform(particleMatrix, 0.0f);

/*
// Again, we could animate the particle's motion as well to rotate slightly over time if we wanted to
// Generally you'll want more than 3 samples for position over time

float particleMatrixPreviousFrame[16] = 
{
	0.99619f, 0.0f, 0.087156f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	0.087156f, 0.0f, 0.99619f, 0.0f,
	1.0f, 0.0f, 0.0f, 1.0f,
};

particleXForm.add_transform(lightMatrixPreviousFrame, -1.0f);

float particleMatrixNextFrame[16] = 
{
	0.99619f, 0.0f, -0.087156f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	-0.087156f, 0.0f, 0.99619f, 0.0f,
	51.0f, 0.0f, 0.0f, 1.0f,
};

particleXForm.add_transform(lightMatrixPreviousFrame, 1.0f);

*/

particleStream.set_transform(particleXForm);

krakRenderer.add_particle_stream(particleStream);

// this method will be removed in future released of KrakatoaSR, but for now its an easy way to get the render output
// note that the extension for the file MUST be exr, no other formats are currently supported (this will change eventually)
krakRenderer.set_output_image_file(outputFilename.c_str());

// we can optionally listen for image data from krakatoa by implementing a frame_buffer_interface, and passing it into the renderer.
// This could also be used to pass data to some kind of a virtual frame buffer to get a preview view of the render.
my_frame_buffer_interface myFBI;

krakRenderer.set_frame_buffer_update( &myFBI );

// finally, call the render
krakRenderer.render();

return EXIT_SUCCESS;

}[/code]

Its maybe a little more complex than necessary, but I wanted to at least show all the basic features. I hope this helps, and if so we’ll look at including this and maybe some other examples in future releases of the API. Again, thanks for the feedback so far everyone.

  • Stephen Kiazyk

Great!

I managed to create my very first rendering out of maya, well its complete red without particles, but it works :slight_smile:

Hi,

is there a way to interrupt the render process somehow?

Hi,

I have some problems with the set_frame_buffer() method.
It works fine and in my rendering it’s called twice with the settings I used in the earlier posting. The first call seem to contain the correct data, at least most of the pixels are red as required with background color. But the second call contains almost only black pixels. I could read that the set_frame_buffer() is called during rendering and at the end. So I suppose the second call is done with the finished image.

On disk, the image is completly red as I expected using a background color of 1,0,0 - of course with watermarks. The UI image I update with the set_frame_buffer() is black.

Any ideas?

Yes, same forum for the time being.

This would be an interesting extension to Krakatoa. I can see how it would be useful in specific cases. It would require some major changes internally to support something like this, so I don’t want to make this a high priority unless there’s some significant interest.

However, something I have been thinking of doing is caching in-memory particles so that particles wouldn’t have to be reloaded or regenerated between subsequent renders. This would be fairly easy implement by adding “cache_particles(bool)” and “clear_cache()” type functions to the renderer class. Would that be of interest to anyone?

We aren’t planning on including Magma in SR for the first release, but our goal is to have near feature parity with MX for the first release.

If you want to do simple Magma-style operations (such as copying channels, scaling channels, setting channels), that is currently available in SR. They haven’t been exposed to the API yet, but will be soon.

Yes. I am working on implementing our file saving mechanism. This will replace the current “set_output_image_file” call. The new way of saving will allow users direct access to the rendered image data if they so request. In SMK’s post, he mentioned using the “set_frame_buffer_update” to do this. That can be used currently, but it is sort of a hack and I would recommend against it at the moment.

No. But this is a great idea. I will try to implement this by the first release. I would do it as a virtual “bool has_been_cancelled()” that the user could implement. It would essentially work the same as the “Cancel” button in the MX version.

This sounds like it a bug that might be my fault. I will test it to see what’s up.

Also, if you are getting random crashing in Maya when writing an SR plugin. It is probably because there is an uncaught exception being thrown by SR. I would recommend wrapping all the SR calls in a try/catch block, then printing out the error that Krakatoa is sending you. Since I’m using standard exceptions, your code could look something like this:

try {
    //krakatoa calls here
} catch( std::exception& e ) {
	std::cout << e.what() << std::endl; //the "e" object contains the exception message from krakatoa
}