|
@@ -0,0 +1,579 @@
|
|
|
+// +----------------------------------------------------------------------
|
|
|
+// | Project : ray.
|
|
|
+// | All rights reserved.
|
|
|
+// +----------------------------------------------------------------------
|
|
|
+// | Copyright (c) 2013-2017.
|
|
|
+// +----------------------------------------------------------------------
|
|
|
+// | * Redistribution and use of this software in source and binary forms,
|
|
|
+// | with or without modification, are permitted provided that the following
|
|
|
+// | conditions are met:
|
|
|
+// |
|
|
|
+// | * Redistributions of source code must retain the above
|
|
|
+// | copyright notice, this list of conditions and the
|
|
|
+// | following disclaimer.
|
|
|
+// |
|
|
|
+// | * Redistributions in binary form must reproduce the above
|
|
|
+// | copyright notice, this list of conditions and the
|
|
|
+// | following disclaimer in the documentation and/or other
|
|
|
+// | materials provided with the distribution.
|
|
|
+// |
|
|
|
+// | * Neither the name of the ray team, nor the names of its
|
|
|
+// | contributors may be used to endorse or promote products
|
|
|
+// | derived from this software without specific prior
|
|
|
+// | written permission of the ray team.
|
|
|
+// |
|
|
|
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+// +----------------------------------------------------------------------
|
|
|
+#include "ies_loader.h"
|
|
|
+#include <assert.h>
|
|
|
+#include <algorithm>
|
|
|
+#include <functional>
|
|
|
+
|
|
|
+IESFileInfo::IESFileInfo()
|
|
|
+ : _cachedIntegral(std::numeric_limits<float>::max())
|
|
|
+ , _error("No data loaded")
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+IESFileInfo::valid() const
|
|
|
+{
|
|
|
+ return _error.empty();
|
|
|
+}
|
|
|
+
|
|
|
+const std::string&
|
|
|
+IESFileInfo::error() const
|
|
|
+{
|
|
|
+ return _error;
|
|
|
+}
|
|
|
+
|
|
|
+IESLoadHelper::IESLoadHelper()
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+IESLoadHelper::~IESLoadHelper()
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+IESLoadHelper::load(const char* data, std::size_t dataLength, IESFileInfo& info)
|
|
|
+{
|
|
|
+ assert(!info.valid());
|
|
|
+ return this->load(std::string(data, dataLength), info);
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+IESLoadHelper::load(const std::string& data, IESFileInfo& info)
|
|
|
+{
|
|
|
+ assert(!info.valid());
|
|
|
+
|
|
|
+ std::string dataPos;
|
|
|
+
|
|
|
+ std::string version;
|
|
|
+ this->getLineContent(data, dataPos, version, false, false);
|
|
|
+
|
|
|
+ if (version.empty())
|
|
|
+ {
|
|
|
+ info._error = "Unknown IES version";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else if (version == "IESNA:LM-63-1995")
|
|
|
+ info._version = "IESNA:LM-63-1995";
|
|
|
+ else if (version == "IESNA91")
|
|
|
+ info._version = "IESNA91";
|
|
|
+ else if (version == "IESNA:LM-63-2002")
|
|
|
+ info._version = "IESNA:LM-63-2002";
|
|
|
+ else
|
|
|
+ info._version = version;
|
|
|
+
|
|
|
+ while (!dataPos.empty())
|
|
|
+ {
|
|
|
+ std::string line;
|
|
|
+ this->getLineContent(dataPos, dataPos, line, false, false);
|
|
|
+
|
|
|
+ if (line.compare(0, 9, "TILT=NONE", 9) == 0 ||
|
|
|
+ line.compare(0, 10, "TILT= NONE", 10) == 0 ||
|
|
|
+ line.compare(0, 10, "TILT =NONE", 10) == 0 ||
|
|
|
+ line.compare(0, 11, "TILT = NONE", 11) == 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if (line.compare(0, 5, "TILT=", 5) == 0 ||
|
|
|
+ line.compare(0, 5, "TILT =", 5) == 0)
|
|
|
+ {
|
|
|
+ info._error = "Not supported yet.";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this->getFloat(dataPos, dataPos, info.totalLights);
|
|
|
+ if (info.totalLights < 0 || info.totalLights > std::numeric_limits<short>::max())
|
|
|
+ {
|
|
|
+ info._error = "Light Count is not valid";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->getFloat(dataPos, dataPos, info.totalLumens);
|
|
|
+ if (info.totalLumens < 0)
|
|
|
+ {
|
|
|
+ info._error = "TotalLumens is not positive number";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->getFloat(dataPos, dataPos, info.candalaMult);
|
|
|
+ if (info.candalaMult < 0)
|
|
|
+ {
|
|
|
+ info._error = "CandalaMult is not positive number";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->getInt(dataPos, dataPos, info.anglesNumV);
|
|
|
+ if (info.anglesNumV < 0 || info.anglesNumV > std::numeric_limits<short>::max())
|
|
|
+ {
|
|
|
+ info._error = "VAnglesNum is not valid";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->getInt(dataPos, dataPos, info.anglesNumH);
|
|
|
+ if (info.anglesNumH < 0 || info.anglesNumH > std::numeric_limits<short>::max())
|
|
|
+ {
|
|
|
+ info._error = "HAnglesNum is not valid";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->getInt(dataPos, dataPos, info.typeOfPhotometric);
|
|
|
+ this->getInt(dataPos, dataPos, info.typeOfUnit);
|
|
|
+
|
|
|
+ this->getFloat(dataPos, dataPos, info.width);
|
|
|
+ this->getFloat(dataPos, dataPos, info.length);
|
|
|
+ this->getFloat(dataPos, dataPos, info.height);
|
|
|
+
|
|
|
+ this->getFloat(dataPos, dataPos, info.ballastFactor);
|
|
|
+ this->getFloat(dataPos, dataPos, info.futureUse);
|
|
|
+ this->getFloat(dataPos, dataPos, info.inputWatts);
|
|
|
+
|
|
|
+ float minSoFarV = std::numeric_limits<float>::lowest();
|
|
|
+ float minSoFarH = std::numeric_limits<float>::lowest();
|
|
|
+
|
|
|
+ info._anglesV.reserve(info.anglesNumV);
|
|
|
+ info._anglesH.reserve(info.anglesNumH);
|
|
|
+
|
|
|
+ for (std::int32_t y = 0; y < info.anglesNumV; ++y)
|
|
|
+ {
|
|
|
+ float value;
|
|
|
+ this->getFloat(dataPos, dataPos, value, true, true);
|
|
|
+
|
|
|
+ if (value < minSoFarV)
|
|
|
+ {
|
|
|
+ info._error = "V Values is not valid";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ minSoFarV = value;
|
|
|
+ info._anglesV.push_back(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (std::int32_t x = 0; x < info.anglesNumH; ++x)
|
|
|
+ {
|
|
|
+ float value;
|
|
|
+ this->getFloat(dataPos, dataPos, value, true, true);
|
|
|
+
|
|
|
+ if (value < minSoFarH)
|
|
|
+ {
|
|
|
+ info._error = "H Values is not valid";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ minSoFarH = value;
|
|
|
+ info._anglesH.push_back(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ info._candalaValues.reserve(info.anglesNumH * info.anglesNumV);
|
|
|
+
|
|
|
+ for (std::int32_t y = 0; y < info.anglesNumH; ++y)
|
|
|
+ {
|
|
|
+ for (std::int32_t x = 0; x < info.anglesNumV; ++x)
|
|
|
+ {
|
|
|
+ float value;
|
|
|
+ this->getFloat(dataPos, dataPos, value, true, true);
|
|
|
+ info._candalaValues.push_back(value * info.candalaMult);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ skipSpaceAndLineEnd(dataPos, dataPos);
|
|
|
+
|
|
|
+ if (!dataPos.empty())
|
|
|
+ {
|
|
|
+ std::string line;
|
|
|
+ this->getLineContent(dataPos, dataPos, line, true, false);
|
|
|
+
|
|
|
+ if (line == "END")
|
|
|
+ skipSpaceAndLineEnd(dataPos, dataPos);
|
|
|
+
|
|
|
+ if (!dataPos.empty())
|
|
|
+ {
|
|
|
+ info._error = "Unexpected content after END.";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ info._error.clear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+IESLoadHelper::saveAs1D(const IESFileInfo& info, float* data, std::uint32_t width, std::uint8_t channel) noexcept
|
|
|
+{
|
|
|
+ assert(data);
|
|
|
+ assert(width > 0);
|
|
|
+ assert(channel == 1 || channel == 3 || channel == 4);
|
|
|
+ assert(info.valid());
|
|
|
+
|
|
|
+ float invW = 1.0f / width;
|
|
|
+ float invMaxValue = this->computeInvMax(info._candalaValues);
|
|
|
+
|
|
|
+ for (std::uint32_t x = 0; x < width; ++x)
|
|
|
+ {
|
|
|
+ float fraction = x * invW;
|
|
|
+ float value = invMaxValue * interpolate1D(info, fraction * 180.0f);
|
|
|
+
|
|
|
+ switch (channel)
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ *data++ = value;
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = 1.0f;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+IESLoadHelper::saveAs2D(const IESFileInfo& info, float* data, std::uint32_t width, std::uint32_t height, std::uint8_t channel) noexcept
|
|
|
+{
|
|
|
+ assert(data);
|
|
|
+ assert(width > 0 && height > 0);
|
|
|
+ assert(channel == 1 || channel == 3 || channel == 4);
|
|
|
+ assert(info.valid());
|
|
|
+
|
|
|
+ float invW = 1.0f / width;
|
|
|
+ float invH = 1.0f / height;
|
|
|
+ float invMaxValue = this->computeInvMax(info._candalaValues);
|
|
|
+
|
|
|
+ for (std::uint32_t y = 0; y < height; ++y)
|
|
|
+ {
|
|
|
+ for (std::uint32_t x = 0; x < width; ++x)
|
|
|
+ {
|
|
|
+ float fractionV = x * invW * 180.0f;
|
|
|
+ float fractionH = y * invH * 180.0f;
|
|
|
+ float value = invMaxValue * interpolate2D(info, fractionV, fractionH);
|
|
|
+
|
|
|
+ switch (channel)
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ *data++ = value;
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = 1.0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+IESLoadHelper::saveAsPreview(const IESFileInfo& info, std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint8_t channel) noexcept
|
|
|
+{
|
|
|
+ assert(data);
|
|
|
+ assert(width > 0 && height > 0);
|
|
|
+ assert(channel == 1 || channel == 3 || channel == 4);
|
|
|
+ assert(info.valid());
|
|
|
+
|
|
|
+ std::vector<float> ies(256);
|
|
|
+ if (!this->saveAs1D(info, ies.data(), ies.size(), 1))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ float maxValue = this->computeInvMax(info._candalaValues);
|
|
|
+
|
|
|
+ auto TonemapHable = [](float x)
|
|
|
+ {
|
|
|
+ const float A = 0.22f;
|
|
|
+ const float B = 0.30f;
|
|
|
+ const float C = 0.10f;
|
|
|
+ const float D = 0.20f;
|
|
|
+ const float E = 0.01f;
|
|
|
+ const float F = 0.30f;
|
|
|
+ return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F;
|
|
|
+ };
|
|
|
+
|
|
|
+ for (int y = 0; y < height; y++)
|
|
|
+ {
|
|
|
+ for (int x = 0; x < width; x++)
|
|
|
+ {
|
|
|
+ float u = ((float)x / width) * 2.0f - 1.0f;
|
|
|
+ float v = 1.0f - ((float)y / height) * 2.0f - 1.0f;
|
|
|
+
|
|
|
+ u *= 2.2f;
|
|
|
+ v *= 2.4f;
|
|
|
+
|
|
|
+ // float3(0.0f, 0.0f, -0.5f) - ray::float3(u, v, 0.0f)
|
|
|
+ float lx = +0.0f - u;
|
|
|
+ float ly = +0.0f - v;
|
|
|
+ float lz = -0.5f - 0.0f;
|
|
|
+
|
|
|
+ // normalize
|
|
|
+ float length = std::sqrt(lx * lx + ly * ly + lz * lz);
|
|
|
+ lx /= length;
|
|
|
+ ly /= length;
|
|
|
+ lz /= length;
|
|
|
+
|
|
|
+ float angle = 1.0 - std::acos(lx * 0.0 + ly * -1.0 + lz * 0.0f) / 3.141592654;
|
|
|
+
|
|
|
+ float intensity = ies[angle * 255] * maxValue / length;
|
|
|
+
|
|
|
+ std::uint8_t value = std::min(std::max((int)std::floor(TonemapHable(intensity) / TonemapHable(maxValue) * 255.0f), 0), 255);
|
|
|
+
|
|
|
+ switch (channel)
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ *data++ = value;
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = value;
|
|
|
+ *data++ = 1.0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+float
|
|
|
+IESLoadHelper::computeInvMax(const std::vector<float>& candalaValues) const
|
|
|
+{
|
|
|
+ assert(candalaValues.size());
|
|
|
+
|
|
|
+ float candala = *std::max_element(candalaValues.begin(), candalaValues.end());
|
|
|
+ return 1.0f / candala;
|
|
|
+}
|
|
|
+
|
|
|
+float
|
|
|
+IESLoadHelper::computeFilterPos(float value, const std::vector<float>& angles) const
|
|
|
+{
|
|
|
+ assert(angles.size());
|
|
|
+
|
|
|
+ std::size_t start = 0;
|
|
|
+ std::size_t end = angles.size() - 1;
|
|
|
+
|
|
|
+ if (value < angles[start]) return 0.0f;
|
|
|
+ if (value > angles[end]) return (float)end;
|
|
|
+
|
|
|
+ while (start < end)
|
|
|
+ {
|
|
|
+ std::size_t index = (start + end + 1) / 2;
|
|
|
+
|
|
|
+ float angle = angles[index];
|
|
|
+ if (value >= angle)
|
|
|
+ {
|
|
|
+ assert(start != index);
|
|
|
+ start = index;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ assert(end != index - 1);
|
|
|
+ end = index - 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ float leftValue = angles[start];
|
|
|
+ float fraction = 0.0f;
|
|
|
+
|
|
|
+ if (start + 1 < (std::uint32_t)angles.size())
|
|
|
+ {
|
|
|
+ float rightValue = angles[start + 1];
|
|
|
+ float deltaValue = rightValue - leftValue;
|
|
|
+
|
|
|
+ if (deltaValue > 0.0001f)
|
|
|
+ {
|
|
|
+ fraction = (value - leftValue) / deltaValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return start + fraction;
|
|
|
+}
|
|
|
+
|
|
|
+float
|
|
|
+IESLoadHelper::interpolate1D(const IESFileInfo& info, float angle) const
|
|
|
+{
|
|
|
+ float angleV = this->computeFilterPos(angle, info._anglesV);
|
|
|
+ float anglesNum = (float)info._anglesH.size();
|
|
|
+ float angleTotal = 0.0f;
|
|
|
+
|
|
|
+ for (float x = 0; x < anglesNum; x++)
|
|
|
+ angleTotal += this->interpolateBilinear(info, x, angleV);
|
|
|
+
|
|
|
+ return angleTotal / anglesNum;
|
|
|
+}
|
|
|
+
|
|
|
+float
|
|
|
+IESLoadHelper::interpolate2D(const IESFileInfo& info, float angleV, float angleH) const
|
|
|
+{
|
|
|
+ float u = this->computeFilterPos(angleH, info._anglesH);
|
|
|
+ float v = this->computeFilterPos(angleV, info._anglesV);
|
|
|
+ return this->interpolateBilinear(info, u, v);
|
|
|
+}
|
|
|
+
|
|
|
+float
|
|
|
+IESLoadHelper::interpolatePoint(const IESFileInfo& info, std::uint32_t x, std::uint32_t y) const
|
|
|
+{
|
|
|
+ assert(x >= 0);
|
|
|
+ assert(y >= 0);
|
|
|
+
|
|
|
+ std::size_t anglesNumH = info._anglesH.size();
|
|
|
+ std::size_t anglesNumV = info._anglesV.size();
|
|
|
+
|
|
|
+ x %= anglesNumH;
|
|
|
+ y %= anglesNumV;
|
|
|
+
|
|
|
+ assert(x < anglesNumH);
|
|
|
+ assert(y < anglesNumV);
|
|
|
+
|
|
|
+ return info._candalaValues[y + anglesNumV * x];
|
|
|
+}
|
|
|
+
|
|
|
+float
|
|
|
+IESLoadHelper::interpolateBilinear(const IESFileInfo& info, float x, float y) const
|
|
|
+{
|
|
|
+ int ix = (int)std::floor(x);
|
|
|
+ int iy = (int)std::floor(y);
|
|
|
+
|
|
|
+ float fracX = x - ix;
|
|
|
+ float fracY = y - iy;
|
|
|
+
|
|
|
+ float p00 = this->interpolatePoint(info, ix + 0, iy + 0);
|
|
|
+ float p10 = this->interpolatePoint(info, ix + 1, iy + 0);
|
|
|
+ float p01 = this->interpolatePoint(info, ix + 0, iy + 1);
|
|
|
+ float p11 = this->interpolatePoint(info, ix + 1, iy + 1);
|
|
|
+
|
|
|
+ auto lerp = [](float t1, float t2, float t3) -> float { return t1 + (t2 - t1) * t3; };
|
|
|
+
|
|
|
+ float p0 = lerp(p00, p01, fracY);
|
|
|
+ float p1 = lerp(p10, p11, fracY);
|
|
|
+
|
|
|
+ return lerp(p0, p1, fracX);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+IESLoadHelper::skipSpaceAndLineEnd(const std::string& data, std::string& out, bool stopOnComma)
|
|
|
+{
|
|
|
+ std::size_t dataBegin = 0;
|
|
|
+ std::size_t dataEnd = data.size();
|
|
|
+
|
|
|
+ while (dataBegin < dataEnd)
|
|
|
+ {
|
|
|
+ if (data[dataBegin] != '\r' && data[dataBegin] != '\n' && data[dataBegin] > ' ')
|
|
|
+ break;
|
|
|
+ dataBegin++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (stopOnComma)
|
|
|
+ {
|
|
|
+ while (dataBegin < dataEnd)
|
|
|
+ {
|
|
|
+ if (data[dataBegin] != ',')
|
|
|
+ break;
|
|
|
+ dataBegin++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ out = data.substr(dataBegin, data.size() - dataBegin);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+IESLoadHelper::getLineContent(const std::string& data, std::string& next, std::string& line, bool stopOnWhiteSpace, bool stopOnComma)
|
|
|
+{
|
|
|
+ skipSpaceAndLineEnd(data, next);
|
|
|
+
|
|
|
+ auto it = data.begin();
|
|
|
+ auto end = data.end();
|
|
|
+
|
|
|
+ for (; it < end; ++it)
|
|
|
+ {
|
|
|
+ if ((*it == '\r') ||
|
|
|
+ (*it == '\n') ||
|
|
|
+ (*it <= ' ' && stopOnWhiteSpace) ||
|
|
|
+ (*it == ',' && stopOnComma))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ line.assign(data, 0, it - data.begin());
|
|
|
+ next.assign(data, it - data.begin(), end - it);
|
|
|
+
|
|
|
+ skipSpaceAndLineEnd(next, next, stopOnComma);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+IESLoadHelper::getFloat(const std::string& data, std::string& next, float& ret, bool stopOnWhiteSpace, bool stopOnComma)
|
|
|
+{
|
|
|
+ std::string line;
|
|
|
+ getLineContent(data, next, line, stopOnWhiteSpace, stopOnComma);
|
|
|
+ assert(!line.empty());
|
|
|
+ ret = (float)std::atof(line.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+IESLoadHelper::getInt(const std::string& data, std::string& next, std::int32_t& ret, bool stopOnWhiteSpace, bool stopOnComma)
|
|
|
+{
|
|
|
+ std::string line;
|
|
|
+ getLineContent(data, next, line, stopOnWhiteSpace, stopOnComma);
|
|
|
+ assert(!line.empty());
|
|
|
+ ret = std::atoi(line.c_str());
|
|
|
+}
|