diff --git a/data/tests/test_luminaire.xml b/data/tests/test_luminaire.xml
new file mode 100644
index 00000000..8b8f7429
--- /dev/null
+++ b/data/tests/test_luminaire.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/src/tests/test_chisquare.cpp b/src/tests/test_chisquare.cpp
index 8b4433d7..55a6c730 100644
--- a/src/tests/test_chisquare.cpp
+++ b/src/tests/test_chisquare.cpp
@@ -39,9 +39,9 @@
MTS_NAMESPACE_BEGIN
/**
- * This testcase checks if the sampling methods of various BSDF & phase
- * function implementations really do what they promise in their pdf()
- * methods
+ * This testcase checks if the sampling methods of various BSDF & phase
+ * function & luminaire implementations really do what they promise in
+ * their pdf() methods
*/
class TestChiSquare : public TestCase {
public:
@@ -299,6 +299,62 @@ public:
Float m_largestWeight;
};
+ /// Adapter to use luminaires in the chi-square test
+ class LuminaireAdapter {
+ public:
+ LuminaireAdapter(
+ const Luminaire *luminaire, Sampler *sampler)
+ : m_luminaire(luminaire), m_sampler(sampler) { }
+
+ boost::tuple generateSample() {
+ Point2 sample(m_sampler->next2D());
+ EmissionRecord pRec(m_mRec, m_wi);
+
+ #if defined(MTS_DEBUG_FP)
+ enableFPExceptions();
+ #endif
+
+ /* Check the various sampling routines for agreement amongst each other */
+ Float pdfVal;
+ Spectrum value = m_luminaire->sampleEmission(eRec, m_sampler->next2D(), m_sampler->next2D());
+
+ if (min < ERROR_REQ && err > ERROR_REQ) // absolute error threshold
+ mismatch = true;
+ else if (min > ERROR_REQ && err/min > ERROR_REQ) // relative error threshold
+ mismatch = true;
+
+ if (mismatch)
+ Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
+ f, pdfVal, sampled);
+
+ #if defined(MTS_DEBUG_FP)
+ disableFPExceptions();
+ #endif
+ return boost::make_tuple(pRec.wo, 1.0f, ESolidAngle);
+ }
+
+ Float pdf(const Vector &wo, EMeasure measure) const {
+ if (measure != ESolidAngle)
+ return 0.0f;
+
+ EmissionRecord pRec(m_mRec, m_wi, wo);
+ #if defined(MTS_DEBUG_FP)
+ enableFPExceptions();
+ #endif
+ if (m_luminaire->eval(pRec) == 0)
+ return 0.0f;
+ Float result = m_luminaire->pdf(pRec);
+ #if defined(MTS_DEBUG_FP)
+ disableFPExceptions();
+ #endif
+ return result;
+ }
+
+ private:
+ ref m_luminaire;
+ ref m_sampler;
+ };
+
void test01_BSDF() {
/* Load a set of BSDF instances to be tested from the following XML file */
ref scene = loadScene("data/tests/test_bsdf.xml");
@@ -410,7 +466,7 @@ public:
}
void test02_PhaseFunction() {
- /* Load a set of BSDF instances to be tested from the following XML file */
+ /* Load a set of phase function instances to be tested from the following XML file */
ref scene = loadScene("data/tests/test_phase.xml");
const std::vector objects = scene->getReferencedObjects();
@@ -472,6 +528,60 @@ public:
Log(EInfo, "%i/%i phase function checks succeeded", testCount-failureCount, testCount);
delete progress;
}
+
+ void test03_Luminaire() {
+ /* Load a set of luminaire instances to be tested from the following XML file */
+ ref scene = loadScene("data/tests/test_luminaire.xml");
+
+ const std::vector objects = scene->getReferencedObjects();
+ size_t thetaBins = 10, wiSamples = 20, failureCount = 0, testCount = 0;
+ ref sampler = static_cast (PluginManager::getInstance()->
+ createObject(MTS_CLASS(Sampler), Properties("independent")));
+
+ ProgressReporter *progress = new ProgressReporter("Checking", wiSamples, NULL);
+
+ Log(EInfo, "Verifying luminaire sampling routines ..");
+ for (size_t i=0; igetClass()->derivesFrom(MTS_CLASS(Luminaire)))
+ continue;
+
+ const Luminaire *phase = static_cast(objects[i]);
+
+ Log(EInfo, "Processing phase function model %s", phase->toString().c_str());
+ Log(EInfo, "Checking the model for %i incident directions", wiSamples);
+ progress->reset();
+
+ /* Test for a number of different incident directions */
+ for (size_t j=0; j chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
+ chiSqr->setLogLevel(EDebug);
+
+ // Initialize the tables used by the chi-square test
+ chiSqr->fill(
+ boost::bind(&LuminaireAdapter::generateSample, &adapter),
+ boost::bind(&LuminaireAdapter::pdf, &adapter, _1, _2)
+ );
+
+ // (the following assumes that the distribution has 1 parameter, e.g. exponent value)
+ ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
+ if (result == ChiSquare::EReject) {
+ std::string filename = formatString("failure_%i.m", failureCount++);
+ chiSqr->dumpTables(filename);
+ failAndContinue(formatString("Uh oh, the chi-square test indicates a potential "
+ "issue for wi=%s. Dumped the contingency tables to '%s' for user analysis",
+ wi.toString().c_str(), filename.c_str()));
+ } else {
+ succeed();
+ }
+ ++testCount;
+ progress->update(j+1);
+ }
+ }
+ Log(EInfo, "%i/%i luminaire checks succeeded", testCount-failureCount, testCount);
+ delete progress;
+ }
+
};
MTS_EXPORT_TESTCASE(TestChiSquare, "Chi-square test for various sampling functions")