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 the camera transform and settings
-
-
-
- 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.