Esempio n. 1
0
bool PathEdge::pathConnect(const Scene *scene, const PathEdge *predEdge,
		const PathVertex *vs, Path &result, const PathVertex *vt,
		const PathEdge *succEdge, int maxInteractions, MemoryPool &pool) {
	BDAssert(result.edgeCount() == 0 && result.vertexCount() == 0);

	if (vs->isEmitterSupernode() || vt->isSensorSupernode()) {
		Float radianceTransport   = vt->isSensorSupernode() ? 1.0f : 0.0f,
		      importanceTransport = 1-radianceTransport;
		PathEdge *edge = pool.allocEdge();
		edge->medium = NULL;
		edge->length = 0.0f;
		edge->d = Vector(0.0f);
		edge->pdf[ERadiance]   = radianceTransport;
		edge->pdf[EImportance] = importanceTransport;
		edge->weight[ERadiance] = Spectrum(radianceTransport);
		edge->weight[EImportance] = Spectrum(importanceTransport);
		result.append(edge);
	} else {
		Point vsp = vs->getPosition(), vtp = vt->getPosition();
		Vector d(vsp-vtp);
		Float remaining = d.length();
		d /= remaining;
		if (remaining == 0) {
			#if defined(MTS_BD_DEBUG)
				SLog(EWarn, "Tried to connect %s and %s, which are located at exactly the same position!",
					vs->toString().c_str(), vt->toString().c_str());
			#endif
			return false;
		}

		Float lengthFactor = vs->isOnSurface() ? (1-ShadowEpsilon) : 1;
		Ray ray(vtp, d, vt->isOnSurface() ? Epsilon : 0,
				remaining * lengthFactor, vs->getTime());
		const Medium *medium = vt->getTargetMedium(succEdge,  d);

		int interactions = 0;

		Intersection its;
		while (true) {
			bool surface = scene->rayIntersectAll(ray, its);

			if (surface && (interactions == maxInteractions ||
				!(its.getBSDF()->getType() & BSDF::ENull))) {
				/* Encountered an occluder -- zero transmittance. */
				result.release(pool);
				return false;
			}

			/* Construct an edge */
			PathEdge *edge = pool.allocEdge();
			result.append(edge);
			edge->length = std::min(its.t, remaining);
			edge->medium = medium;
			edge->d = d;

			if (medium) {
				MediumSamplingRecord mRec;
				medium->eval(Ray(ray, 0, edge->length), mRec);
				edge->pdf[ERadiance] = (surface || !vs->isMediumInteraction())
					? mRec.pdfFailure : mRec.pdfSuccess;
				edge->pdf[EImportance] = (interactions > 0 || !vt->isMediumInteraction())
					? mRec.pdfFailure : mRec.pdfSuccessRev;

				if (edge->pdf[ERadiance] == 0 || edge->pdf[EImportance] == 0
						|| mRec.transmittance.isZero()) {
					/* Zero transmittance */
					result.release(pool);
					return false;
				}
				edge->weight[EImportance] = mRec.transmittance / edge->pdf[EImportance];
				edge->weight[ERadiance]   = mRec.transmittance / edge->pdf[ERadiance];
			} else {
				edge->weight[ERadiance] = edge->weight[EImportance] = Spectrum(1.0f);
				edge->pdf[ERadiance] = edge->pdf[EImportance] = 1.0f;
			}

			if (!surface || remaining - its.t < 0)
				break;

			/* Advance the ray */
			ray.o = ray(its.t);
			remaining -= its.t;
			ray.mint = Epsilon;
			ray.maxt = remaining * lengthFactor;

			const BSDF *bsdf = its.getBSDF();

			/* Account for the ENull interaction */
			Vector wo = its.toLocal(ray.d);
			BSDFSamplingRecord bRec(its, -wo, wo, ERadiance);
			bRec.component = BSDF::ENull;
			Float nullPdf = bsdf->pdf(bRec, EDiscrete);
			if (nullPdf == 0) {
				result.release(pool);
				return false;
			}

			PathVertex *vertex = pool.allocVertex();
			vertex->type = PathVertex::ESurfaceInteraction;
			vertex->degenerate = !(bsdf->hasComponent(BSDF::ESmooth)
				|| its.shape->isEmitter() || its.shape->isSensor());
			vertex->measure = EDiscrete;
			vertex->componentType = BSDF::ENull;
			vertex->pdf[EImportance] = vertex->pdf[ERadiance] = nullPdf;
			vertex->weight[EImportance] = vertex->weight[ERadiance]
				= bsdf->eval(bRec, EDiscrete) / nullPdf;
			vertex->rrWeight = 1.0f;
			vertex->getIntersection() = its;
			result.append(vertex);

			if (its.isMediumTransition()) {
				const Medium *expected = its.getTargetMedium(-ray.d);
				if (medium != expected) {
					#if defined(MTS_BD_TRACE)
						SLog(EWarn, "PathEdge::pathConnect(): attempted two connect "
							"two vertices that disagree about the medium in between! "
							"Please check your scene for leaks.");
					#endif
					++mediumInconsistencies;
					result.release(pool);
					return false;
				}
				medium = its.getTargetMedium(ray.d);
			}

			if (++interactions > 100) { /// Just a precaution..
				SLog(EWarn, "pathConnect(): round-off error issues?");
				result.release(pool);
				return false;
			}
		}

		if (medium != vs->getTargetMedium(predEdge, -d)) {
			#if defined(MTS_BD_TRACE)
				SLog(EWarn, "PathEdge::pathConnect(): attempted two connect "
					"two vertices that disagree about the medium in between! "
					"Please check your scene for leaks.");
			#endif
			++mediumInconsistencies;
			result.release(pool);
			return false;
		}
	}

	result.reverse();

	BDAssert(result.edgeCount() == result.vertexCount() + 1);
	BDAssert((int) result.vertexCount() <= maxInteractions || maxInteractions < 0);

	return true;
}
Esempio n. 2
0
bool PathEdge::pathConnectAndCollapse(const Scene *scene, const PathEdge *predEdge,
		const PathVertex *vs, const PathVertex *vt,
		const PathEdge *succEdge, int &interactions) {
	if (vs->isEmitterSupernode() || vt->isSensorSupernode()) {
		Float radianceTransport   = vt->isSensorSupernode() ? 1.0f : 0.0f,
		      importanceTransport = 1-radianceTransport;
		medium = NULL;
		length = 0.0f;
		d = Vector(0.0f);
		pdf[ERadiance]   = radianceTransport;
		pdf[EImportance] = importanceTransport;
		weight[ERadiance] = Spectrum(radianceTransport);
		weight[EImportance] = Spectrum(importanceTransport);
		interactions = 0;
	} else {
		Point vsp = vs->getPosition(), vtp = vt->getPosition();
		d = vsp-vtp;
		length = d.length();
		int maxInteractions = interactions;
		interactions = 0;

		if (length == 0) {
			#if defined(MTS_BD_DEBUG)
				SLog(EWarn, "Tried to connect %s and %s, which are located at exactly the same position!",
					vs->toString().c_str(), vt->toString().c_str());
			#endif
			return false;
		}

		d /= length;
		Float lengthFactor = vs->isOnSurface() ? (1-ShadowEpsilon) : 1;
		Ray ray(vtp, d, vt->isOnSurface() ? Epsilon : 0, length * lengthFactor, vs->getTime());

		weight[ERadiance] = Spectrum(1.0f);
		weight[EImportance] = Spectrum(1.0f);
		pdf[ERadiance] = 1.0f;
		pdf[EImportance] = 1.0f;

		Intersection its;
		Float remaining = length;
		medium = vt->getTargetMedium(succEdge, d);

		while (true) {
			bool surface = scene->rayIntersectAll(ray, its);

			if (surface && (interactions == maxInteractions ||
				!(its.getBSDF()->getType() & BSDF::ENull))) {
				/* Encountered an occluder -- zero transmittance. */
				return false;
			}

			if (medium) {
				Float segmentLength = std::min(its.t, remaining);
				MediumSamplingRecord mRec;
				medium->eval(Ray(ray, 0, segmentLength), mRec);

				Float pdfRadiance = (surface || !vs->isMediumInteraction())
					? mRec.pdfFailure : mRec.pdfSuccess;
				Float pdfImportance = (interactions > 0 || !vt->isMediumInteraction())
					? mRec.pdfFailure : mRec.pdfSuccessRev;

				if (pdfRadiance == 0 || pdfImportance == 0 || mRec.transmittance.isZero()) {
					/* Zero transmittance */
					return false;
				}

				weight[EImportance] *= mRec.transmittance / pdfImportance;
				weight[ERadiance] *= mRec.transmittance / pdfRadiance;
				pdf[EImportance] *= pdfImportance;
				pdf[ERadiance] *= pdfRadiance;
			}

			if (!surface || remaining - its.t < 0)
				break;

			/* Advance the ray */
			ray.o = ray(its.t);
			remaining -= its.t;
			ray.mint = Epsilon;
			ray.maxt = remaining * lengthFactor;

			/* Account for the ENull interaction */
			const BSDF *bsdf = its.getBSDF();
			Vector wo = its.toLocal(ray.d);
			BSDFSamplingRecord bRec(its, -wo, wo, ERadiance);
			bRec.component = BSDF::ENull;
			Float nullPdf = bsdf->pdf(bRec, EDiscrete);
			if (nullPdf == 0)
				return false;

			Spectrum nullWeight = bsdf->eval(bRec, EDiscrete) / nullPdf;

			weight[EImportance] *= nullWeight;
			weight[ERadiance] *= nullWeight;
			pdf[EImportance] *= nullPdf;
			pdf[ERadiance] *= nullPdf;

			if (its.isMediumTransition()) {
				const Medium *expected = its.getTargetMedium(-ray.d);
				if (medium != expected) {
					#if defined(MTS_BD_TRACE)
						SLog(EWarn, "PathEdge::pathConnectAndCollapse(): attempted two connect "
							"two vertices that disagree about the medium in between! "
							"Please check your scene for leaks.");
					#endif
					++mediumInconsistencies;
					return false;
				}
				medium = its.getTargetMedium(ray.d);
			}

			if (++interactions > 100) { /// Just a precaution..
				SLog(EWarn, "pathConnectAndCollapse(): round-off error issues?");
				return false;
			}
		}

		if (medium != vs->getTargetMedium(predEdge, -d)) {
			#if defined(MTS_BD_TRACE)
				SLog(EWarn, "PathEdge::pathConnectAndCollapse(): attempted two connect "
					"two vertices that disagree about the medium in between! "
					"Please check your scene for leaks.");
			#endif
			++mediumInconsistencies;
			return false;
		}
	}

	return true;
}
Esempio n. 3
0
bool PathEdge::connect(const Scene *scene,
			const PathEdge *predEdge, const PathVertex *vs,
			const PathVertex *vt, const PathEdge *succEdge) {

	if (vs->isEmitterSupernode() || vt->isSensorSupernode()) {
		Float radianceTransport   = vt->isSensorSupernode() ? 1.0f : 0.0f,
		      importanceTransport = 1-radianceTransport;

		medium = NULL;
		d = Vector(0.0f);
		length = 0.0f;
		pdf[ERadiance]   = radianceTransport;
		pdf[EImportance] = importanceTransport;
		weight[ERadiance] = Spectrum(radianceTransport);
		weight[EImportance] = Spectrum(importanceTransport);
	} else {
		Point vsp = vs->getPosition(), vtp = vt->getPosition();
		d = vsp-vtp;
		length = d.length();
		d /= length;

		Ray ray(vtp, d, vt->isOnSurface() ? Epsilon : 0, length *
			(vs->isOnSurface() ? (1-ShadowEpsilon) : 1), vs->getTime());

		/* Check for occlusion */
		if (scene->rayIntersectAll(ray))
			return false;

		const Medium *vtMedium = vt->getTargetMedium(succEdge, d);
		const Medium *vsMedium = vs->getTargetMedium(predEdge, -d);

		if (vsMedium != vtMedium) {
			#if defined(MTS_BD_TRACE)
				SLog(EWarn, "PathEdge::connect(): attempted two connect "
					"two vertices that disagree about the medium in between! "
					"Please check your scene for leaks.");
			#endif
			++mediumInconsistencies;
			return false;
		}

		medium = vtMedium;

		if (medium) {
			MediumSamplingRecord mRec;
			medium->eval(ray, mRec);

			pdf[EImportance] = vt->isMediumInteraction() ? mRec.pdfSuccessRev : mRec.pdfFailure;
			pdf[ERadiance]   = vs->isMediumInteraction() ? mRec.pdfSuccess    : mRec.pdfFailure;

			/* Fail if there is no throughput */
			if (mRec.transmittance.isZero() || pdf[EImportance] == 0 || pdf[ERadiance] == 0)
				return false;

			weight[EImportance] = mRec.transmittance / pdf[EImportance];
			weight[ERadiance]   = mRec.transmittance / pdf[ERadiance];
		} else {
			weight[ERadiance] = weight[EImportance] = Spectrum(1.0f);
			pdf[ERadiance] = pdf[EImportance] = 1.0f;
		}
	}

	return true;
}
int importMain(int argc, char **argv) {
	bool srgb = false, mapSmallerSide = true;
	int optchar;
	char *end_ptr = NULL;
	int xres = -1, yres = -1;
	std::string filmType = "hdrfilm";
	FileResolver *fileResolver = Thread::getThread()->getFileResolver();
	ELogLevel logLevel = EInfo;
	bool packGeometry = true, importMaterials = true,
		 importAnimations = false;

	optind = 1;

	while ((optchar = getopt(argc, argv, "snzvyhmr:a:l:")) != -1) {
		switch (optchar) {
			case 'a': {
					std::vector<std::string> paths = tokenize(optarg, ";");
					for (int i=(int) paths.size()-1; i>=0; --i)
						fileResolver->prependPath(paths[i]);
				}
				break;
			case 's':
				srgb = true;
				break;
			case 'm':
				mapSmallerSide = false;
				break;
			case 'n':
				importMaterials = false;
				break;
			case 'z':
				importAnimations = true;
				break;
			case 'v':
				logLevel = EDebug;
				break;
			case 'l':
				filmType = optarg;
				break;
			case 'y':
				packGeometry = false;
				break;
			case 'r': {
					std::vector<std::string> tokens = tokenize(optarg, "x");
					if (tokens.size() != 2)
						SLog(EError, "Invalid resolution argument supplied!");
					xres = strtol(tokens[0].c_str(), &end_ptr, 10);
					if (*end_ptr != '\0')
						SLog(EError, "Invalid resolution argument supplied!");
					yres = strtol(tokens[1].c_str(), &end_ptr, 10);
					if (*end_ptr != '\0')
						SLog(EError, "Invalid resolution argument supplied!");
				}
				break;
			case 'h':
			default:
				help();
				return -1;
	}
	};

	if (argc-optind < 2) {
		help();
		return -1;
	}

	ref<Logger> log = Thread::getThread()->getLogger();
	log->setLogLevel(logLevel);

	ConsoleGeometryConverter converter;
	converter.setSRGB(srgb);
	converter.setResolution(xres, yres);
	converter.setImportMaterials(importMaterials);
	converter.setImportAnimations(importAnimations);
	converter.setMapSmallerSide(mapSmallerSide);
	converter.setPackGeometry(packGeometry);
	converter.setFilmType(filmType);

	const Logger *logger = Thread::getThread()->getLogger();
	size_t initialWarningCount = logger->getWarningCount();
	converter.convert(argv[optind], "", argv[optind+1], argc > optind+2 ? argv[optind+2] : "");
	size_t warningCount = logger->getWarningCount() - initialWarningCount;

	if (warningCount > 0)
		SLog(EInfo, "Encountered " SIZE_T_FMT " warnings -- please check the "
			"messages above for details.", warningCount);

	return 0;
}
Esempio n. 5
0
static void setProperties(QDomDocument &doc, QDomElement &element,
		const Properties &props) {
	element.setAttribute("type", props.getPluginName().c_str());

	std::vector<std::string> propertyNames;
	props.putPropertyNames(propertyNames);
	for (std::vector<std::string>::const_iterator it = propertyNames.begin();
		it != propertyNames.end(); ++it) {
		QDomElement property;
		switch (props.getType(*it)) {
			case Properties::EBoolean:
				property = doc.createElement("boolean");
				property.setAttribute("value", props.getBoolean(*it) ? "true" : "false");
				break;
			case Properties::EInteger:
				property = doc.createElement("integer");
				property.setAttribute("value", QString::number(props.getInteger(*it)));
				break;
			case Properties::EFloat:
				property = doc.createElement("float");
				property.setAttribute("value", QString::number(props.getFloat(*it)));
				break;
			case Properties::EString:
				property = doc.createElement("string");
				property.setAttribute("value", props.getString(*it).c_str());
				break;
			case Properties::EAnimatedTransform: {
					const AnimatedTransform *trafo = props.getAnimatedTransform(*it);

					std::set<Float> times;
					trafo->collectKeyframes(times);

					property = doc.createElement("animation");

					for (std::set<Float>::iterator it2 = times.begin(); it2 != times.end(); ++it2) {
						const Matrix4x4 &matrix = trafo->eval(*it2).getMatrix();
						QDomElement trafoTag = doc.createElement("transform");
						QDomElement matrixTag = doc.createElement("matrix");

						QString value;
						for (int i=0; i<4; ++i)
							for (int j=0; j<4; ++j)
								value += QString("%1 ").arg(matrix(i, j));

						matrixTag.setAttribute("value", value);
						trafoTag.setAttribute("time", *it2);
						trafoTag.appendChild(matrixTag);
						property.appendChild(trafoTag);
					}
				}
				break;
			case Properties::ETransform: {
					/* Captures the subset of transformations that are used by
					   Mitsuba's perspective and orthographic camera classes */
					property = doc.createElement("transform");
					Transform trafo = props.getTransform(*it);
					if (trafo.hasScale()) {
						QDomElement scale = doc.createElement("scale");
						Float valueX = trafo(Vector(1, 0, 0)).length();
						Float valueY = trafo(Vector(0, 1, 0)).length();
						Float valueZ = trafo(Vector(0, 0, 1)).length();
						if (std::abs(1-valueX) < 1e-3f)
							valueX = 1.0f;
						if (std::abs(1-valueY) < 1e-3f)
							valueY = 1.0f;
						if (std::abs(1-valueZ) < 1e-3f)
							valueZ = 1.0f;
						scale.setAttribute("x", QString("%1").arg(valueX));
						scale.setAttribute("y", QString("%1").arg(valueY));
						scale.setAttribute("z", QString("%1").arg(valueZ));
						property.appendChild(scale);
						trafo = trafo * Transform::scale(Vector(1.0f/valueX, 1.0f/valueY, 1.0f/valueZ));
					}
					QDomElement lookAt = doc.createElement("lookat");
					property.appendChild(lookAt);
					if (trafo.det3x3() < 0) {
						QDomElement scale = doc.createElement("scale");
						scale.setAttribute("x", "-1");
						property.insertBefore(scale, lookAt);
					}
					Point p = trafo(Point(0,0,0));
					Point t = p + trafo(Vector(0,0,1));
					Vector u = trafo(Vector(0,1,0));

					lookAt.setAttribute("origin", QString("%1, %2, %3").arg(p.x).arg(p.y).arg(p.z));
					lookAt.setAttribute("up",     QString("%1, %2, %3").arg(u.x).arg(u.y).arg(u.z));
					lookAt.setAttribute("target", QString("%1, %2, %3").arg(t.x).arg(t.y).arg(t.z));
				}
				break;
			default:
				SLog(EError, "setProperties(): \"%s\": Unable to handle elements of type %i",
					(*it).c_str(), props.getType(*it));
		}
		property.setAttribute("name", (*it).c_str());
		element.appendChild(property);
	}
}
Esempio n. 6
0
void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
	QDomElement root = ctx->doc.documentElement();

	// ====================================================================
	//   Serialize the sensor configuration
	// ====================================================================

	QList<QDomElement> oldSensors = findAllChildren(root, "sensor");

	const ref_vector<Sensor> sensors = ctx->scene->getSensors();
	ref_vector<Sensor>::const_iterator it = std::find(sensors.begin(),
			sensors.end(), ctx->scene->getSensor());
	if (it == sensors.end())
		SLog(EError, "Number of sensors did not match between loaded scene and XML file!");

	QDomElement sensor, oldSensor;

	if (oldSensors.size() == 0)
		; // ok -- scene did not contain a sensor before
	else if ((size_t) oldSensors.size() != ctx->scene->getSensors().size())
		SLog(EError, "Number of sensors did not match between loaded scene and XML file!");
	else
		oldSensor = oldSensors[it-sensors.begin()];

	if (oldSensor.isNull()) {
		sensor = ctx->doc.createElement("sensor");
		root.insertAfter(sensor, QDomNode());
	} else {
		sensor = ctx->doc.createElement("sensor");
		root.insertAfter(sensor, oldSensor);
		root.removeChild(oldSensor);
	}

	setProperties(ctx->doc, sensor, ctx->scene->getSensor()->getProperties());

	// ====================================================================
	//   Serialize the sampler configuration
	// ====================================================================

	QDomElement sampler = ctx->doc.createElement("sampler");
	sensor.appendChild(sampler);

	setProperties(ctx->doc, sampler,
		ctx->scene->getSampler()->getProperties());

	// ====================================================================
	//   Serialize the film configuration
	// ====================================================================

	QDomElement film = ctx->doc.createElement("film");
	sensor.appendChild(film);

	Properties filmProps(ctx->scene->getFilm()->getProperties());
	if (filmProps.getPluginName() == "ldrfilm") {
		/* Also export the tonemapper settings */
		if (ctx->toneMappingMethod == EGamma) {
			filmProps.setString("tonemapMethod", "gamma", false);
			filmProps.setFloat("exposure", ctx->exposure, false);
			filmProps.removeProperty("key");
			filmProps.removeProperty("burn");
		} else {
			filmProps.setString("tonemapMethod", "reinhard", false);
			filmProps.setFloat("key", ctx->reinhardKey, false);
			filmProps.setFloat("burn", (ctx->reinhardBurn + 10) / 20.0f, false);
			filmProps.removeProperty("exposure");
		}
		filmProps.setFloat("gamma", ctx->srgb ? -1 : ctx->gamma, false);
	}

	setProperties(ctx->doc, film, filmProps);

	// ====================================================================
	//   Serialize the reconstruction filter configuration
	// ====================================================================

	QDomElement rfilter = ctx->doc.createElement("rfilter");
	film.appendChild(rfilter);

	setProperties(ctx->doc, rfilter,
		ctx->scene->getFilm()->getReconstructionFilter()->getProperties());

	// ====================================================================
	//   Serialize medium references of the sensor
	// ====================================================================

	QList<QDomElement> oldSensorReferences = findAllChildren(oldSensor, "ref");
	oldSensorReferences.append(findAllChildren(oldSensor, "medium"));

	for (int i=0; i<oldSensorReferences.size(); ++i)
		sensor.appendChild(ctx->doc.importNode(oldSensorReferences[i], true));

	// ====================================================================
	//   Serialize the integrator configuration
	// ====================================================================

	QDomElement oldIntegratorNode = findUniqueChild(root, "integrator");
	QDomElement newIntegratorNode = ctx->doc.createElement("integrator");

	const Integrator *integrator = ctx->scene->getIntegrator();
	setProperties(ctx->doc, newIntegratorNode, integrator->getProperties());
	processSubIntegrators(ctx->doc, integrator, newIntegratorNode);

	root.insertBefore(newIntegratorNode, oldIntegratorNode);
	root.removeChild(oldIntegratorNode);

	QFile file;
	file.setFileName(targetFile);
	if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
		QMessageBox::critical(parent, parent->tr("Unable to save"),
			parent->tr("Unable to save changes: could not open the destination file <b>%1</b>.").arg(targetFile),
			QMessageBox::Ok);
		return;
	}

	/* Clean up the XML output generated by Qt so that it
	   is more suitable for human consumption ..
	   Beware: the code below is tailored to Qt's
	   output and won't work on arbitrary XML files */
	QString textContent = ctx->doc.toString();
	QTextStream input(&textContent);
	QTextStream output(&file);
	cleanupXML(input, output);
	file.close();
}
Esempio n. 7
0
BrentSolver::Result BrentSolver::solve(const boost::function<Float (Float)> &f,
			 Float x0, Float y0,
			 Float x1, Float y1,
			 Float x2, Float y2) const {
	Float delta = x1 - x0;
	Float oldDelta = delta;

	size_t i = 0;
	while (i < m_maxIterations) {
		if (std::abs(y2) < std::abs(y1)) {
			// use the bracket point if is better than last approximation
			x0 = x1;
			x1 = x2;
			x2 = x0;
			y0 = y1;
			y1 = y2;
			y2 = y0;
		}
		if (std::abs(y1) <= m_absAccuracy) {
			// Avoid division by very small values. Assume
			// the iteration has converged (the problem may
			// still be ill conditioned)
			return Result(true, i, x1, y1);
		}
		Float dx = x2 - x1;
		Float tolerance =
			std::max(m_relAccuracyPos * std::abs(x1), m_absAccuracyPos);

		if (std::abs(dx) <= tolerance) 
			return Result(true, i, x1, y1);
		if ((std::abs(oldDelta) < tolerance) ||
				(std::abs(y0) <= std::abs(y1))) {
			// Force bisection.
			delta = (Float) 0.5f * dx;
			oldDelta = delta;
		} else {
			Float r3 = y1 / y0;
			Float p;
			Float p1;
			// the equality test (x0 == x2) is intentional,
			// it is part of the original Brent's method,
			// it should NOT be replaced by proximity test
			if (x0 == x2) {
				// Linear interpolation.
				p = dx * r3;
				p1 = 1 - r3;
			} else {
				// Inverse quadratic interpolation.
				Float r1 = y0 / y2;
				Float r2 = y1 / y2;
				p = r3 * (dx * r1 * (r1 - r2) - (x1 - x0) * (r2 - 1));
				p1 = (r1 - 1) * (r2 - 1) * (r3 - 1);
			}
			if (p > 0) {
				p1 = -p1;
			} else {
				p = -p;
			}
			if (2 * p >= (Float) 1.5f * dx * p1 - std::abs(tolerance * p1) ||
					p >= std::abs((Float) 0.5f * oldDelta * p1)) {
				// Inverse quadratic interpolation gives a value
				// in the wrong direction, or progress is slow.
				// Fall back to bisection.
				delta = (Float) 0.5f * dx;
				oldDelta = delta;
			} else {
				oldDelta = delta;
				delta = p / p1;
			}
		}
		// Save old X1, Y1
		x0 = x1;
		y0 = y1;
		// Compute new X1, Y1
		if (std::abs(delta) > tolerance) {
			x1 = x1 + delta;
		} else if (dx > 0) {
			x1 = x1 + (Float) 0.5f * tolerance;
		} else if (dx <= 0) {
			x1 = x1 - (Float) 0.5f * tolerance;
		}
		y1 = f(x1);
		if ((y1 > 0) == (y2 > 0)) {
			x2 = x0;
			y2 = y0;
			delta = x1 - x0;
			oldDelta = delta;
		}
		i++;
	}
	SLog(EWarn, "BrentSolver: Maximum number of iterations (" SIZE_T_FMT ") exceeded!",
		m_maxIterations);
	return Result(false, i, x1, y1);
}