Developing plugins

Plugins are extensions to the HQR software which serve a particular user purpose but may not be needed by everybody. For this reason plugins are particularly suitable to those who want to beneficiate from the core of the program to test additional algorithm but without any modification of the base component of HQR.

One example is calculation of local radiance fields: the user needs to be able to select a light path (see at right) and extend it to a local set of lines. This set is based on the loaded scene geometry and should be drawn somehow into the graphical window. For this, an interface is needed to make the plugin communicate with HQR.

Plugin interface

The interface for developing plugins works like this:

Using this method the plugin can inject some functionalities (derived from a pre-defined type) into HQR, and all variables in HQR are available to the plugin. Most of these variables are accessed through the HQR::DataManager class.

Existing entry-points are listed in the table below:

Name of entry-pointParametersUse
void HQR_BUNDLE_draw()voidcalled after drawing the rest of the scene. Used for drawing some data specific to the plugin
void HQR_BUNDLE_select()const HQR::GlobalIntersectionStructure& P called when selecting an object.
void HQR_BUNDLE_click()const HQR::Point3& P,
const HQR::NVector3& d
called when the mouse clicks in the drawing window. P is the point corresponding to the eye and d the direction of the ray.
QWidget *HQR_BUNDLE_buildInterface()voidcalled at interface creation. The supplied widget will be added in the main panel. Use it to create your own interface functionalities. One widget per plugin is allowed.
bool HQR_BUNDLE_takesJob()const HQR::Config& CThis function is provided for the HQR command-line use of the plugin. It is passed the Config structure corresponding to the job file passed as argument to the executable, and should only return true or false depending if it accepts to perform the job described by the JOB_TYPE variable. See Base/Config.h for how to get variable values in Config structures. Use this function to have your plugin take priority over the default job.
void HQR_BUNDLE_performJob()const HQR::Config& CAlso provided for the HQR command-line use of the plugin. This function should contain the actions the plugin may want to do on a scene in replacement of the classical photon mapping computation. Its own variables are to be accessed in the passed Config structure.
void HQR_BUNDLE_nonPrehemptiveJob()const HQR::Config& CThis function is provided for anyone who wants to read configuration values in the config file, but still letting HQR perform its regular job. This happens e.g. if you modify a structure in the program (e.g. the Global Intersector) and want to setup parameters accordingly.
bool HQR_BUNDLE_saveConfig()std::ofstream& file For saving a config file, the plugin may either call Interface::saveConfig(const char *job_type), passing the name of the job the plugin is made for (if one wants to setup a specific job name for the output .hqr file) or let HQR call Interface::saveConfig("STILL_IMAGE") which is the default. This name will figure in the JOBE_TYPE variable of the config file and will allow the plugin to handle this config file correctly (see the HQR_BUNDLE_takesJob() function above). After saving its own variables into the config file file, Interface::saveConfig() calls HQR_BUNDLE_saveConfig(), passing the file ofstream to the plugin so that it can add its own variables to the config file. As output, true must be returned if no error were encountered while saving, false otherwise.
unsigned int HQR_BUNDLE_disableInterfaces()voidShould return an ORed value of HQR::Interface::InterfaceType values for preventing the corresponding interface panels to be added to the usual control panel.
HQR::RayTracer *HQR_BUNDLE_allocateRayTracer()voidCalled for replacing the default raytracer
HQR::PhotonMapCreator *HQR_BUNDLE_allocatePhotonMapCreator()voidCalled for replacing the default photonmap creator
HQR::PhotonMap *HQR_BUNDLE_allocatePhotonMap()voidCalled for replacing the default photonmap
HQR::GlobalIntersectionStructure *HQR_BUNDLE_allocateGlobalIntersectionStructure()voidCalled for replacing the default global intersection structure

Notes:

Of course, not all possible entry points have been coded up to now, but adding a new one on request will not take long.

Example

The SurfaceLightField plugin which can be found in the HQR/Plugins/SurfaceLightField directory, has been implemented that way. The goal of the plugin is to be able to compute the 4D incident or reflected lightfield attached to an object with texture coordinates. The plugin thus needs:

All this can be achieved using the HQR plugin entry points, as shown in the piece of the original code below from the SLFBundle.cpp file:
#include <iostream>
#include <qwidget.h>

#include <GL/gl.h>

// -======================================================- //
//               References to HQR headers                  //
// -======================================================- //

#include <Base/Config.h>
#include <Geometry/Point3.h>
#include <Geometry/NVector3.h>
#include <Geometry/Ray.h>
#include <Geometry/GPIndexedFaceSet.h>
#include <Kernel/DataManager.h>
#include <RayTracer/RayTracer.h>
#include <HQR/ttyRasterCallback.h>
#include <GUI/Interface.h>

namespace HQR
{
	class Point3 ;
	class NVector3 ;
	class SNObject ;
}

// -======================================================- //
//                      Local headers                       //
// -======================================================- //

#include "SLFBundle.h"
#include "SLFCreator.h"
#include "SLF_Wimpl.h"

// -======================================================- //
//        Interface for dynamic loading into the GUI        //
// -======================================================- //

QWidget *HQR_BUNDLE_buildInterface()
{
	return SLFBundle::getInterface() ;
}

unsigned int HQR_BUNDLE_suppressInterfaces()
{
	return Interface::Interface_StillImageCreator ;
}

void HQR_BUNDLE_select(const HQR::Point3&,const HQR::NVector3&,const HQR::NVector3&,const HQR::NVector3&,const HQR::SNObject *sno)
{
	SLFBundle::getInterface()->updateSelectedObject(sno) ;
}

bool HQR_BUNDLE_saveConfig(const char *job_type,ofstream& file)
{
	if(!strcmp(job_type,"SLF"))
	{
		SLFBundle::getInterface()->saveConfig(file) ;
		return true ;
	}
	else
		return false ;
}

bool HQR_BUNDLE_takesJob(const HQR::Config& C)
{
	if(!strcmp(C.getStringValue("JOB_TYPE",""),"SLF"))
		return true ;
	else
		return false ;
}

void HQR_BUNDLE_performJob(const HQR::Config& C)
{
	try
	{
		const char *name = C.getStringValue("SLF_NAME_TEMPLATE","") ;

		if(strlen(name) == 0)
		{
			name = "no_name" ;
			SWARNING << "No name supplied for output slf (SLF_NAME_TEMPLATE variable). \"no_name\" will be used." << endl ;
		}
		DataManager::preparePhotonMaps() ;

		DataManager::getRayTracer()->setEpsilon(DataManager::getGlobalEpsilon()) ;

		const char *obj_name = C.getStringValue("SLF_OBJECT_NAME","") ;

		if(strlen(obj_name) == 0)
			throw runtime_error("No name given for object to treat. Use the SLF_OBJECT_NAME variable.") ;

		const SNObject *sno = DataManager::findObjectInSceneGraph(obj_name) ;

		if(sno == NULL) 
			throw runtime_error(string("No object called '")+obj_name+"'. Giving up.") ;

		const GPIndexedFaceSet *gpi = dynamic_cast<const GPIndexedFaceSet *>(sno->getPrimitive()) ; 

		if(gpi == NULL) 
			throw runtime_error(string("Object called '")+obj_name+"' is not an indexed face set. Giving up.") ;

		if(gpi->getTexCoord().empty()) 
			throw runtime_error(string("Object called '")+obj_name+"' does not have texture coordinates. Giving up.") ;

		int W = C.getIntValue("SLF_WIDTH",100) ;
		int H = C.getIntValue("SLF_HEIGHT",100) ;
		int T = C.getIntValue("SLF_NTHETA",50) ;
		int P = C.getIntValue("SLF_NPHI",100) ;

		bool incident = (!strcmp(C.getStringValue("SLF_DIRECTION","INCIDENT"),"INCIDENT"))?1:0;
		int nb_proc = C.getIntValue("NUMBER_OF_CONCURRENT_JOBS",1) ;

		DataManager::getRayTracer()->shutUpOnSkippedPixels(true) ;

		ttyRasterCallback rstb ;
		SLFCreator::computeAndSaveSLF(name,nb_proc,gpi,W,H,T,P,&rstb,incident) ;
	}
	catch(exception& e)
	{
		cout << "Job raised an exception: " << e.what() << endl ;
		return ;
	}
}

// -======================================================- //
//        Storage of variables of the plugin                //
// -======================================================- //

SLF_Wimpl *SLFBundle::getInterface()
{
	static SLF_Wimpl *_int = NULL ;

	if(_int == NULL)
		_int = new SLF_Wimpl() ;

	return _int ;
}

This file mainly contains the entry points to link the HQR software to the SurfaLightField extension. Because entry points can not be class members, they themselves call static members of a class (the SLFBundle class) which contains the very functionalities of the plugin.

The complete source of the plugin is in the Plugins/SurfaceLightField directory. It is possible to test it using:

      GUI Build/i686-linux-g++/plugins/libSLF.so

The HQR exe should output a line which reads:

      [INFO] Loaded bundle Build/i686-linux-g++/plugins/libSLF.so

Note: do not forget to put the absolute path of the lib, or at least a leading './' before it. Otherwise you get an error like:

      [ERROR] libSLFBundle.so: cannot open shared object file: No such file or directory

The interface should know contain the added widget:


The proper use of plugins

The use of plugins in the current state of the program has some limits however. Functionalities which touch the very core of the algorithm may not be easy to implement using the provided interface. For instance building a plugin to treat BRDF's would need to register many functionalities in the parsing library as well as in the core of the rendering algorithm. However, such a functionality may be considered to be included in the core of HQR for the same reasons. Some easy-to implement plugins include:


Valid XHTML 1.0! Valid CSS! Last modified on Thursday, March 16, 2005.