mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-08-16 04:15:02 -04:00
1.0.0.2 12/05/2016
--User changes -Add many tooltips to help clarify functionality. -Select multiple flames in library for del/move. Still only one allowed to be set as the current. -Show checkbox for current flame. Remember this is not necessarily what's selected. -User can now drag a square to select xforms, which keeps in sync with checkboxes. -Remove --nframes from command line. Replace with new params: --loopframes, --interpframes, --interploops. -Add two new options to EmberGenome: --cwloops --cwinterploops to specify whether rotation should go clockwise instead of the default counter clockwise. -Add these to Fractorium as checkboxes. -Apply All now also works for toggling animate flag on xforms. -Options dialog now allows user to set whether double click toggles spinners, or right click does. --Bug fixes -Selecting final and non-final xforms, and then dragging the non-final did not drag the final with it. -Selecting all xforms when a final was present, then deleting crashed the program. -Remove support for ppm files in the command line programs, it's an outdated format. -Switching between SP and DP kept reapplying the palette adjustments. --Code changes -Move build system to Visual Studio 2015 and Qt 5.6. -SSE used during addition of points to the histogram. -Remove last remnants of old flam3 C code and replace with C++. -Remove unused code involving tbb::task_group. -Make settings object a global shared_ptr singleton, so it doesn't have to be passed around.
This commit is contained in:
@ -18,9 +18,6 @@
|
||||
#define _stat stat
|
||||
#define _fstat fstat
|
||||
#define _stricmp strcmp
|
||||
#define sscanf_s sscanf
|
||||
#define sprintf_s snprintf
|
||||
#define snprintf_s snprintf
|
||||
typedef int errno_t;
|
||||
#endif
|
||||
|
||||
@ -40,7 +37,7 @@ static void sincos(float x, float* s, float* c)
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
#define EMBER_VERSION "1.0.0.1"
|
||||
#define EMBER_VERSION "1.0.0.2"
|
||||
#define EPS6 T(1e-6)
|
||||
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
|
||||
#define ISAAC_SIZE 4
|
||||
|
@ -13,8 +13,6 @@
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4251; disable : 4661; disable : 4100)
|
||||
#define basename(x) _strdup(x)
|
||||
#define snprintf _snprintf
|
||||
#define snprintf_s _snprintf_s
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define EMBER_OS "WIN"
|
||||
|
||||
@ -69,11 +67,12 @@
|
||||
#endif
|
||||
|
||||
//Intel's Threading Building Blocks is what's used for all threading.
|
||||
#include <tbb/task_group.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
#define GLM_FORCE_RADIANS 1
|
||||
#define GLM_ENABLE_EXPERIMENTAL 1
|
||||
|
||||
#ifndef __APPLE__
|
||||
#define GLM_FORCE_INLINE 1
|
||||
#endif
|
||||
|
@ -121,9 +121,6 @@ public:
|
||||
b = false;
|
||||
}
|
||||
|
||||
if (f.is_open())
|
||||
f.close();
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ public:
|
||||
throw "Out of bounds xform index in selection distribution.";
|
||||
|
||||
#endif
|
||||
//printf("offset = %d, xform = %d, running sum = %f\n", j, i, tempDensity);
|
||||
//cout << "offset = " << j << ", xform = " << i << ", running sum = " << tempDensity << "\n";
|
||||
m_XformDistributions[(distrib * CHOOSE_XFORM_GRAIN) + j] = byte(i);
|
||||
tempDensity += densityPerElement;
|
||||
j++;
|
||||
|
@ -241,7 +241,6 @@ private:
|
||||
/// <param name="palettes">The vector to store the paresed palettes associated with this file in.</param>
|
||||
void ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes)
|
||||
{
|
||||
bool hexError = false;
|
||||
char* val;
|
||||
const char* loc = __FUNCTION__;
|
||||
xmlAttrPtr attr;
|
||||
@ -259,33 +258,31 @@ private:
|
||||
|
||||
if (!Compare(attr->name, "data"))
|
||||
{
|
||||
int colorIndex = 0;
|
||||
uint r, g, b;
|
||||
int colorCount = 0;
|
||||
hexError = false;
|
||||
string s1, s;
|
||||
size_t tmp, colorCount = 0, colorIndex = 0;
|
||||
stringstream ss, temp(val); ss >> std::hex;
|
||||
s.reserve(2048);
|
||||
|
||||
do
|
||||
while (temp >> s1)
|
||||
s += s1;
|
||||
|
||||
auto length = s.size();
|
||||
|
||||
for (size_t strIndex = 0; strIndex < length;)
|
||||
{
|
||||
int ret = sscanf_s(static_cast<char*>(&(val[colorIndex])), "00%2x%2x%2x", &r, &g, &b);
|
||||
strIndex += 2;//Skip past the 00 at the beginning of each RGB.
|
||||
|
||||
if (ret != 3)
|
||||
for (glm::length_t i = 0; i < 3 && colorCount < palette.Size(); i++)
|
||||
{
|
||||
AddToReport(string(loc) + " : Problem reading hexadecimal color data " + string(&val[colorIndex]));
|
||||
hexError = true;
|
||||
break;
|
||||
const char tmpStr[3] = { s[strIndex++], s[strIndex++], 0 };//Read out and convert the string two characters at a time.
|
||||
ss.clear();//Reset and fill the string stream.
|
||||
ss.str(tmpStr);
|
||||
ss >> tmp;//Do the conversion.
|
||||
palette.m_Entries[colorCount][i] = T(tmp) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
}
|
||||
|
||||
colorIndex += 8;
|
||||
|
||||
while (isspace(int(val[colorIndex])))
|
||||
colorIndex++;
|
||||
|
||||
palette[colorCount].r = T(r) / T(255);//Store as normalized colors in the range of 0-1.
|
||||
palette[colorCount].g = T(g) / T(255);
|
||||
palette[colorCount].b = T(b) / T(255);
|
||||
colorCount++;
|
||||
}
|
||||
while (colorCount < COLORMAP_LENGTH);
|
||||
}
|
||||
else if (!Compare(attr->name, "number"))
|
||||
{
|
||||
@ -300,11 +297,8 @@ private:
|
||||
attr = attr->next;
|
||||
}
|
||||
|
||||
if (!hexError)
|
||||
{
|
||||
palette.m_Filename = filename;
|
||||
palettes.push_back(palette);
|
||||
}
|
||||
palette.m_Filename = filename;
|
||||
palettes.push_back(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -354,9 +354,6 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
size_t i, temporalSample = 0;
|
||||
T deTime;
|
||||
auto success = eRenderStatus::RENDER_OK;
|
||||
//double iterationTime = 0;
|
||||
//double accumulationTime = 0;
|
||||
//Timing it;
|
||||
|
||||
//Reset timers and progress percent if: Beginning anew or only filtering and/or accumulating.
|
||||
if (!resume || accumOnly || filterAndAccumOnly)
|
||||
@ -1294,6 +1291,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
size_t totalItersPerThread = size_t(ceil(double(iterCount) / double(m_ThreadsToUse)));
|
||||
double percent, etaMs;
|
||||
EmberStats stats;
|
||||
//vector<double> accumTimes(4);
|
||||
|
||||
//Do this every iteration for an animation, or else do it once for a single image. CPU only.
|
||||
if (!m_LastIter)
|
||||
@ -1302,106 +1300,91 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
m_ThreadEmbers.insert(m_ThreadEmbers.begin(), m_ThreadsToUse, m_Ember);
|
||||
}
|
||||
|
||||
#ifdef TG
|
||||
size_t threadIndex;
|
||||
|
||||
for (size_t i = 0; i < m_ThreadsToUse; i++)
|
||||
{
|
||||
threadIndex = i;
|
||||
m_TaskGroup.run([&, threadIndex] ()
|
||||
{
|
||||
#else
|
||||
parallel_for(size_t(0), m_ThreadsToUse, [&] (size_t threadIndex)
|
||||
{
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
SetThreadPriority(GetCurrentThread(), int(m_Priority));
|
||||
SetThreadPriority(GetCurrentThread(), int(m_Priority));
|
||||
#elif defined(__APPLE__)
|
||||
sched_param sp = {0};
|
||||
sp.sched_priority = m_Priority;
|
||||
pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
|
||||
sched_param sp = {0};
|
||||
sp.sched_priority = m_Priority;
|
||||
pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
|
||||
#else
|
||||
pthread_setschedprio(pthread_self(), int(m_Priority));
|
||||
pthread_setschedprio(pthread_self(), int(m_Priority));
|
||||
#endif
|
||||
//Timing t;
|
||||
IterParams<T> params;
|
||||
m_BadVals[threadIndex] = 0;
|
||||
params.m_Count = std::min(totalItersPerThread, SubBatchSize());
|
||||
params.m_Skip = FuseCount();
|
||||
//params.m_OneColDiv2 = m_CarToRas.OneCol() / 2;
|
||||
//params.m_OneRowDiv2 = m_CarToRas.OneRow() / 2;
|
||||
//Timing t;
|
||||
IterParams<T> params;
|
||||
m_BadVals[threadIndex] = 0;
|
||||
params.m_Count = std::min(totalItersPerThread, SubBatchSize());
|
||||
params.m_Skip = FuseCount();
|
||||
//params.m_OneColDiv2 = m_CarToRas.OneCol() / 2;
|
||||
//params.m_OneRowDiv2 = m_CarToRas.OneRow() / 2;
|
||||
|
||||
//Sub batch iterations, loop 2.
|
||||
for (m_SubBatch[threadIndex] = 0; (m_SubBatch[threadIndex] < totalItersPerThread) && !m_Abort; m_SubBatch[threadIndex] += params.m_Count)
|
||||
//Sub batch iterations, loop 2.
|
||||
for (m_SubBatch[threadIndex] = 0; (m_SubBatch[threadIndex] < totalItersPerThread) && !m_Abort; m_SubBatch[threadIndex] += params.m_Count)
|
||||
{
|
||||
//Must recalculate the number of iters to run on each sub batch because the last batch will most likely have less than SubBatchSize iters.
|
||||
//For example, if 51,000 are requested, and the sbs is 10,000, it should run 5 sub batches of 10,000 iters, and one final sub batch of 1,000 iters.
|
||||
params.m_Count = std::min(params.m_Count, totalItersPerThread - m_SubBatch[threadIndex]);
|
||||
//Use first as random point, the rest are iterated points.
|
||||
//Note that this gets reset with a new random point for each subBatchSize iterations.
|
||||
//This helps correct if iteration happens to be on a bad trajectory.
|
||||
m_Samples[threadIndex][0].m_X = m_Rand[threadIndex].template Frand11<T>();
|
||||
m_Samples[threadIndex][0].m_Y = m_Rand[threadIndex].template Frand11<T>();
|
||||
m_Samples[threadIndex][0].m_Z = 0;//m_Ember.m_CamZPos;//Apo set this to 0, then made the user use special variations to kick it. It seems easier to just set it to zpos.
|
||||
m_Samples[threadIndex][0].m_ColorX = m_Rand[threadIndex].template Frand01<T>();
|
||||
//Finally, iterate.
|
||||
//t.Tic();
|
||||
//Iterating, loop 3.
|
||||
m_BadVals[threadIndex] += m_Iterator->Iterate(m_ThreadEmbers[threadIndex], params, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
|
||||
//m_BadVals[threadIndex] += m_Iterator->Iterate(m_Ember, params, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
|
||||
//iterationTime += t.Toc();
|
||||
|
||||
if (m_LockAccum)
|
||||
m_AccumCs.lock();
|
||||
|
||||
//t.Tic();
|
||||
//Map temp buffer samples into the histogram using the palette for color.
|
||||
Accumulate(m_Rand[threadIndex], m_Samples[threadIndex].data(), params.m_Count, &m_Dmap);
|
||||
//accumTimes[threadIndex] += t.Toc();
|
||||
|
||||
if (m_LockAccum)
|
||||
m_AccumCs.unlock();
|
||||
|
||||
if (m_Callback && threadIndex == 0)
|
||||
{
|
||||
//Must recalculate the number of iters to run on each sub batch because the last batch will most likely have less than SubBatchSize iters.
|
||||
//For example, if 51,000 are requested, and the sbs is 10,000, it should run 5 sub batches of 10,000 iters, and one final sub batch of 1,000 iters.
|
||||
params.m_Count = std::min(params.m_Count, totalItersPerThread - m_SubBatch[threadIndex]);
|
||||
//Use first as random point, the rest are iterated points.
|
||||
//Note that this gets reset with a new random point for each subBatchSize iterations.
|
||||
//This helps correct if iteration happens to be on a bad trajectory.
|
||||
m_Samples[threadIndex][0].m_X = m_Rand[threadIndex].template Frand11<T>();
|
||||
m_Samples[threadIndex][0].m_Y = m_Rand[threadIndex].template Frand11<T>();
|
||||
m_Samples[threadIndex][0].m_Z = 0;//m_Ember.m_CamZPos;//Apo set this to 0, then made the user use special variations to kick it. It seems easier to just set it to zpos.
|
||||
m_Samples[threadIndex][0].m_ColorX = m_Rand[threadIndex].template Frand01<T>();
|
||||
//Finally, iterate.
|
||||
//t.Tic();
|
||||
//Iterating, loop 3.
|
||||
m_BadVals[threadIndex] += m_Iterator->Iterate(m_ThreadEmbers[threadIndex], params, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
|
||||
//m_BadVals[threadIndex] += m_Iterator->Iterate(m_Ember, params, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
|
||||
//iterationTime += t.Toc();
|
||||
|
||||
if (m_LockAccum)
|
||||
m_AccumCs.lock();
|
||||
|
||||
//t.Tic();
|
||||
//Map temp buffer samples into the histogram using the palette for color.
|
||||
Accumulate(m_Rand[threadIndex], m_Samples[threadIndex].data(), params.m_Count, &m_Dmap);
|
||||
|
||||
//accumulationTime += t.Toc();
|
||||
if (m_LockAccum)
|
||||
m_AccumCs.unlock();
|
||||
|
||||
if (m_Callback && threadIndex == 0)
|
||||
{
|
||||
percent = 100.0 *
|
||||
percent = 100.0 *
|
||||
double
|
||||
(
|
||||
double
|
||||
(
|
||||
double
|
||||
(
|
||||
double
|
||||
(
|
||||
//Takes progress of current thread and multiplies by thread count.
|
||||
//This assumes the threads progress at roughly the same speed.
|
||||
double(m_LastIter + (m_SubBatch[threadIndex] * m_ThreadsToUse)) / double(ItersPerTemporalSample())
|
||||
) + temporalSample
|
||||
) / double(TemporalSamples())
|
||||
);
|
||||
double percentDiff = percent - m_LastIterPercent;
|
||||
double toc = m_ProgressTimer.Toc();
|
||||
//Takes progress of current thread and multiplies by thread count.
|
||||
//This assumes the threads progress at roughly the same speed.
|
||||
double(m_LastIter + (m_SubBatch[threadIndex] * m_ThreadsToUse)) / double(ItersPerTemporalSample())
|
||||
) + temporalSample
|
||||
) / double(TemporalSamples())
|
||||
);
|
||||
double percentDiff = percent - m_LastIterPercent;
|
||||
double toc = m_ProgressTimer.Toc();
|
||||
|
||||
if (percentDiff >= 10 || (toc > 1000 && percentDiff >= 1))//Call callback function if either 10% has passed, or one second (and 1%).
|
||||
{
|
||||
etaMs = ((100.0 - percent) / percent) * m_RenderTimer.Toc();
|
||||
if (percentDiff >= 10 || (toc > 1000 && percentDiff >= 1))//Call callback function if either 10% has passed, or one second (and 1%).
|
||||
{
|
||||
etaMs = ((100.0 - percent) / percent) * m_RenderTimer.Toc();
|
||||
|
||||
if (!m_Callback->ProgressFunc(m_Ember, m_ProgressParameter, percent, 0, etaMs))
|
||||
Abort();
|
||||
if (!m_Callback->ProgressFunc(m_Ember, m_ProgressParameter, percent, 0, etaMs))
|
||||
Abort();
|
||||
|
||||
m_LastIterPercent = percent;
|
||||
m_ProgressTimer.Tic();
|
||||
}
|
||||
m_LastIterPercent = percent;
|
||||
m_ProgressTimer.Tic();
|
||||
}
|
||||
}
|
||||
});
|
||||
#ifdef TG
|
||||
}
|
||||
|
||||
m_TaskGroup.wait();
|
||||
#endif
|
||||
|
||||
}
|
||||
});
|
||||
stats.m_Iters = std::accumulate(m_SubBatch.begin(), m_SubBatch.end(), 0ULL);//Sum of iter count of all threads.
|
||||
stats.m_Badvals = std::accumulate(m_BadVals.begin(), m_BadVals.end(), 0ULL);
|
||||
stats.m_IterMs = m_IterTimer.Toc();
|
||||
//cout << "Accum time: " << std::accumulate(accumTimes.begin(), accumTimes.end(), 0.0) << endl;
|
||||
//t2.Toc(__FUNCTION__);
|
||||
return stats;
|
||||
}
|
||||
@ -1609,19 +1592,49 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
|
||||
colorIndexFrac = colorIndex - bucketT(intColorIndex);//Interpolate between intColorIndex and intColorIndex + 1.
|
||||
}
|
||||
|
||||
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[histIndex]);//Vectorizer can't tell these point to different locations.
|
||||
const bucketT* __restrict pal = glm::value_ptr(palette->m_Entries[intColorIndex]);
|
||||
const bucketT* __restrict pal2 = glm::value_ptr(palette->m_Entries[intColorIndex + 1]);
|
||||
auto cifm1 = bucketT(1) - colorIndexFrac;
|
||||
|
||||
//Loops are unrolled to allow auto vectorization.
|
||||
if (p.m_VizAdjusted == 1)
|
||||
m_HistBuckets[histIndex] += ((dmap[intColorIndex] * (1 - colorIndexFrac)) + (dmap[intColorIndex + 1] * colorIndexFrac));
|
||||
{
|
||||
hist[0] += (pal[0] * cifm1) + (pal2[0] * colorIndexFrac);
|
||||
hist[1] += (pal[1] * cifm1) + (pal2[1] * colorIndexFrac);
|
||||
hist[2] += (pal[2] * cifm1) + (pal2[2] * colorIndexFrac);
|
||||
hist[3] += (pal[3] * cifm1) + (pal2[3] * colorIndexFrac);
|
||||
}
|
||||
else
|
||||
m_HistBuckets[histIndex] += (((dmap[intColorIndex] * (1 - colorIndexFrac)) + (dmap[intColorIndex + 1] * colorIndexFrac)) * bucketT(p.m_VizAdjusted));
|
||||
{
|
||||
auto va = bucketT(p.m_VizAdjusted);
|
||||
hist[0] += ((pal[0] * cifm1) + (pal2[0] * colorIndexFrac)) * va;
|
||||
hist[1] += ((pal[1] * cifm1) + (pal2[1] * colorIndexFrac)) * va;
|
||||
hist[2] += ((pal[2] * cifm1) + (pal2[2] * colorIndexFrac)) * va;
|
||||
hist[3] += ((pal[3] * cifm1) + (pal2[3] * colorIndexFrac)) * va;
|
||||
}
|
||||
}
|
||||
else if (PaletteMode() == ePaletteMode::PALETTE_STEP)
|
||||
{
|
||||
intColorIndex = Clamp<size_t>(size_t(p.m_ColorX * COLORMAP_LENGTH), 0, COLORMAP_LENGTH_MINUS_1);
|
||||
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[histIndex]);//Vectorizer can't tell these point to different locations.
|
||||
const bucketT* __restrict pal = glm::value_ptr(palette->m_Entries[intColorIndex]);
|
||||
|
||||
if (p.m_VizAdjusted == 1)
|
||||
m_HistBuckets[histIndex] += dmap[intColorIndex];
|
||||
{
|
||||
hist[0] += pal[0];
|
||||
hist[1] += pal[1];
|
||||
hist[2] += pal[2];
|
||||
hist[3] += pal[3];
|
||||
}
|
||||
else
|
||||
m_HistBuckets[histIndex] += (dmap[intColorIndex] * bucketT(p.m_VizAdjusted));
|
||||
{
|
||||
auto va = bucketT(p.m_VizAdjusted);
|
||||
hist[0] += pal[0] * va;
|
||||
hist[1] += pal[1] * va;
|
||||
hist[2] += pal[2] * va;
|
||||
hist[3] += pal[3] * va;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +229,6 @@ protected:
|
||||
vector<size_t> m_SubBatch;
|
||||
vector<size_t> m_BadVals;
|
||||
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> m_Rand;
|
||||
unique_ptr<tbb::task_group> m_TaskGroup = make_unique<tbb::task_group>();
|
||||
std::recursive_mutex m_RenderingCs, m_AccumCs, m_FinalAccumCs, m_ResizeCs;
|
||||
Timing m_RenderTimer, m_IterTimer, m_ProgressTimer;
|
||||
};
|
||||
|
@ -424,7 +424,6 @@ public:
|
||||
size_t i;
|
||||
T t;
|
||||
ostringstream os;
|
||||
char ministr[32];
|
||||
|
||||
if (crossMode == eCrossMode::CROSS_NOT_SPECIFIED)
|
||||
{
|
||||
@ -463,8 +462,7 @@ public:
|
||||
for (i = 0; i < emberOut.TotalXformCount(); i++)
|
||||
emberOut.GetTotalXform(i)->DeleteMotionElements();
|
||||
|
||||
sprintf_s(ministr, 32, "%7.5g", t);
|
||||
os << "cross interpolate " << ministr;
|
||||
os << "cross interpolate " << std::to_string(t);
|
||||
}
|
||||
else//Alternate mode.
|
||||
{
|
||||
@ -953,7 +951,8 @@ public:
|
||||
/// <param name="ember">The ember to rotate</param>
|
||||
/// <param name="rotated">The rotated xform</param>
|
||||
/// <param name="blend">The time percentage value which dictates how much of a percentage of 360 degrees it should be rotated and the time position for the motion elements</param>
|
||||
void Loop(Ember<T>& ember, Ember<T>& rotated, T blend)
|
||||
/// <param name="cw">True to rotate clockwise, else rotate counter clockwise. Ignored if rotations is 0.</param>
|
||||
void Loop(Ember<T>& ember, Ember<T>& rotated, T blend, bool cw)
|
||||
{
|
||||
rotated = ember;
|
||||
|
||||
@ -970,7 +969,7 @@ public:
|
||||
}
|
||||
|
||||
rotated.ApplyFlameMotion(blend);
|
||||
rotated.RotateAffines(-blend * 360);//Rotate the affines.
|
||||
rotated.RotateAffines((cw ? blend : -blend) * 360);//Rotate the affines.
|
||||
rotated.DeleteMotionElements();//Delete all motion elements from the looped ember, at the xform level and at the parent ember level.
|
||||
}
|
||||
|
||||
@ -981,8 +980,10 @@ public:
|
||||
/// <param name="embers">The embers to interpolate</param>
|
||||
/// <param name="result">The result of the interpolation</param>
|
||||
/// <param name="blend">The interpolation time</param>
|
||||
/// <param name="rotations">The number of times to rotate within the interpolation</param>
|
||||
/// <param name="cw">True to rotate clockwise, else rotate counter clockwise. Ignored if rotations is 0.</param>
|
||||
/// <param name="seqFlag">True if embers points to the first or last ember in the entire sequence, else false.</param>
|
||||
void Edge(Ember<T>* embers, Ember<T>& result, T blend, bool seqFlag)
|
||||
void Edge(Ember<T>* embers, Ember<T>& result, T blend, size_t rotations, bool cw, bool seqFlag)
|
||||
{
|
||||
size_t i, si;
|
||||
|
||||
@ -1016,9 +1017,15 @@ public:
|
||||
m_EdgeSpun[1].m_Time = 1;
|
||||
//Call this first to establish the asymmetric reference angles.
|
||||
Interpolater<T>::AsymmetricRefAngles(m_EdgeSpun, 2);
|
||||
|
||||
//Rotate the aligned xforms.
|
||||
m_EdgeSpun[0].RotateAffines(-blend * 360);
|
||||
m_EdgeSpun[1].RotateAffines(-blend * 360);
|
||||
if (rotations)
|
||||
{
|
||||
auto cwblend = cw ? blend : -blend;
|
||||
m_EdgeSpun[0].RotateAffines(cwblend * (360 * rotations));
|
||||
m_EdgeSpun[1].RotateAffines(cwblend * (360 * rotations));
|
||||
}
|
||||
|
||||
m_Interpolater.Interpolate(m_EdgeSpun, 2, m_Smooth ? Interpolater<T>::Smoother(blend) : blend, m_Stagger, result);
|
||||
}
|
||||
|
||||
@ -1037,11 +1044,13 @@ public:
|
||||
/// <param name="result">The result of the spin</param>
|
||||
/// <param name="frame">The frame in the sequence to be stored in the m_Time member of result</param>
|
||||
/// <param name="blend">The interpolation time</param>
|
||||
void Spin(Ember<T>& parent, Ember<T>* templ, Ember<T>& result, size_t frame, T blend)
|
||||
/// <param name="cw">True to rotate clockwise, else rotate counter clockwise. Ignored if rotations is 0.</param>
|
||||
void Spin(Ember<T>& parent, Ember<T>* templ, Ember<T>& result, size_t frame, T blend, bool cw)
|
||||
{
|
||||
char temp[50];
|
||||
auto cwblend = cw ? blend : -blend;
|
||||
string temp = "rotate " + std::to_string(cwblend * 360.0);
|
||||
//Spin the parent blend degrees.
|
||||
Loop(parent, result, blend);
|
||||
Loop(parent, result, blend, cw);
|
||||
|
||||
//Apply the template if necessary.
|
||||
if (templ)
|
||||
@ -1052,14 +1061,12 @@ public:
|
||||
result.m_Interp = eInterp::EMBER_INTERP_LINEAR;
|
||||
result.m_PaletteInterp = ePaletteInterp::INTERP_HSV;
|
||||
//Create the edit doc xml.
|
||||
sprintf_s(temp, 50, "rotate %g", blend * 360.0);
|
||||
result.ClearEdit();
|
||||
result.m_Edits = m_EmberToXml.CreateNewEditdoc(&parent, nullptr, temp, m_Nick, m_Url, m_Id, m_Comment, m_SheepGen, m_SheepId);
|
||||
//Subpixel jitter.
|
||||
Offset(result, m_OffsetX, m_OffsetY);
|
||||
//Make the name of the flame the time.
|
||||
sprintf_s(temp, 50, "%f", result.m_Time);
|
||||
result.m_Name = string(temp);
|
||||
result.m_Name = std::to_string(result.m_Time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1074,11 +1081,14 @@ public:
|
||||
/// <param name="frame">The frame in the sequence to be stored in the m_Time member of result</param>
|
||||
/// <param name="seqFlag">True if embers points to the first or last ember in the entire sequence, else false.</param>
|
||||
/// <param name="blend">The interpolation time</param>
|
||||
void SpinInter(Ember<T>* parents, Ember<T>* templ, Ember<T>& result, size_t frame, bool seqFlag, T blend)
|
||||
/// <param name="rotations">The number of times to rotate within the interpolation</param>
|
||||
/// <param name="cw">True to rotate clockwise, else rotate counter clockwise. Ignored if rotations is 0.</param>
|
||||
void SpinInter(Ember<T>* parents, Ember<T>* templ, Ember<T>& result, size_t frame, bool seqFlag, T blend, size_t rotations, bool cw)
|
||||
{
|
||||
char temp[50];
|
||||
auto cwblend = cw ? blend : -blend;
|
||||
string temp = "interpolate " + std::to_string(cwblend * 360.0);
|
||||
//Interpolate between spun parents.
|
||||
Edge(parents, result, blend, seqFlag);
|
||||
Edge(parents, result, blend, rotations, cw, seqFlag);
|
||||
|
||||
//Original did an interpolated palette hack here for random palettes, but it was never used anywhere so ember omits it.//ORIG
|
||||
|
||||
@ -1089,14 +1099,12 @@ public:
|
||||
//Set ember parameters accordingly.
|
||||
result.m_Time = T(frame);
|
||||
//Create the edit doc xml.
|
||||
sprintf_s(temp, 50, "interpolate %g", blend * 360.0);
|
||||
result.ClearEdit();
|
||||
result.m_Edits = m_EmberToXml.CreateNewEditdoc(&parents[0], &parents[1], temp, m_Nick, m_Url, m_Id, m_Comment, m_SheepGen, m_SheepId);
|
||||
//Subpixel jitter.
|
||||
Offset(result, m_OffsetX, m_OffsetY);
|
||||
//Make the name of the flame the time.
|
||||
sprintf_s(temp, 50, "%f", result.m_Time);
|
||||
result.m_Name = string(temp);
|
||||
result.m_Name = std::to_string(result.m_Time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -222,6 +222,28 @@ public:
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For creating an object without passing parameters.
|
||||
/// When the derived class has a default constructor, this should
|
||||
/// not be called. This is only for when the derived class constructor
|
||||
/// requires arguments. In that case, Instance() must first be called
|
||||
/// with the proper values. Then once the singleton is constructed, this
|
||||
/// can be called to just retrieve the object without having to worry about
|
||||
/// parameters.
|
||||
/// This is enforced by throwing if this has been called before Instance() is called.
|
||||
/// </summary>
|
||||
/// <returns>The constructed object</returns>
|
||||
static std::shared_ptr<T> DefInstance()
|
||||
{
|
||||
auto& staticInstance = GetStaticInstance();
|
||||
auto temp = staticInstance.lock();
|
||||
|
||||
if (!temp)
|
||||
throw "Cannot create singleton with defaults, must first call at least once with proper arguments.";
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Clever hack to get a static to behave like a member variable that can be seen between classes and functions in the hierarchy.
|
||||
@ -255,64 +277,34 @@ protected:
|
||||
/// Open a file in binary mode and read its entire contents into a vector of bytes. Optionally null terminate.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to read</param>
|
||||
/// <param name="buf">The vector which will be populated with the file's contents</param>
|
||||
/// <param name="buf">The string which will be populated with the file's contents</param>
|
||||
/// <param name="nullTerminate">Whether to append a NULL character as the last element of the vector. Needed when reading text files. Default: true.</param>
|
||||
/// <returns>True if successfully read and populated, else false</returns>
|
||||
static bool ReadFile(const char* filename, string& buf, bool nullTerminate = true)
|
||||
{
|
||||
bool b = false;
|
||||
FILE* f = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
fopen_s(&f, filename, "rb");//Open in binary mode.
|
||||
ifstream ifs(filename, ios::binary | ios::ate);
|
||||
auto pos = ifs.tellg();
|
||||
buf.resize(pos + streampos(nullTerminate ? 1 : 0));
|
||||
ifs.seekg(0, ios::beg);
|
||||
ifs.read(&buf[0], pos);
|
||||
|
||||
if (f)
|
||||
{
|
||||
struct _stat statBuf;
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
int statResult = _fstat(f->_file, &statBuf);//Get data associated with file.
|
||||
#else
|
||||
int statResult = _fstat(f->_fileno, &statBuf);//Get data associated with file.
|
||||
#endif
|
||||
if (nullTerminate)//Optionally NULL terminate if they want to treat it as a string.
|
||||
buf[buf.size() - 1] = 0;
|
||||
|
||||
if (statResult == 0)//Check if statistics are valid.
|
||||
{
|
||||
buf.resize(statBuf.st_size + (nullTerminate ? 1 : 0));//Allocate vector to be the size of the entire file, with an optional additional character for nullptr.
|
||||
|
||||
if (buf.size() == static_cast<size_t>(statBuf.st_size + 1))//Ensure allocation succeeded.
|
||||
{
|
||||
size_t bytesRead = fread(&buf[0], 1, statBuf.st_size, f);//Read the entire file at once.
|
||||
|
||||
if (bytesRead == (static_cast<size_t>(statBuf.st_size)))//Ensure the number of bytes read matched what was requested.
|
||||
{
|
||||
if (nullTerminate)//Optionally nullptr terminate if they want to treat it as a string.
|
||||
buf[buf.size() - 1] = 0;
|
||||
|
||||
b = true;//Success.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
f = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
cout << "Error: Reading file " << filename << " failed: " << e.what() << "\n";
|
||||
b = false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cout << "Error: Reading file " << filename << " failed.\n";
|
||||
b = false;
|
||||
}
|
||||
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return b;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -442,42 +442,6 @@ public:
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an integer to a string.
|
||||
/// Just a wrapper around _itoa_s() which wraps the result in a std::string.
|
||||
/// </summary>
|
||||
/// <param name="i">The integer to convert</param>
|
||||
/// <param name="radix">The radix of the integer. Default: 10.</param>
|
||||
/// <returns>The converted string</returns>
|
||||
static string Itos(int i, int radix = 10)
|
||||
{
|
||||
char ch[16];
|
||||
#ifdef _WIN32
|
||||
_itoa_s(i, ch, 16, radix);
|
||||
#else
|
||||
sprintf(ch, "%d", i);
|
||||
#endif
|
||||
return string(ch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an unsigned 64-bit integer to a string.
|
||||
/// Just a wrapper around _ui64toa_s() which wraps the result in a std::string.
|
||||
/// </summary>
|
||||
/// <param name="i">The unsigned 64-bit integer to convert</param>
|
||||
/// <param name="radix">The radix of the integer. Default: 10.</param>
|
||||
/// <returns>The converted string</returns>
|
||||
static string Itos64(size_t i, int radix = 10)
|
||||
{
|
||||
char ch[64];
|
||||
#ifdef _WIN32
|
||||
_ui64toa_s(i, ch, 64, radix);
|
||||
#else
|
||||
sprintf(ch, "%lu", i);
|
||||
#endif
|
||||
return string(ch);
|
||||
}
|
||||
|
||||
static vector<string> m_FlattenNames;
|
||||
|
||||
private:
|
||||
@ -523,7 +487,7 @@ private:
|
||||
if (auto pal = m_PaletteList.GetPalette(PaletteList<T>::m_DefaultFilename, currentEmber.PaletteIndex()))
|
||||
currentEmber.m_Palette = *pal;
|
||||
else
|
||||
AddToReport(string(loc) + " : Error assigning palette with index " + Itos(currentEmber.PaletteIndex()));
|
||||
AddToReport(string(loc) + " : Error assigning palette with index " + std::to_string(currentEmber.PaletteIndex()));
|
||||
}
|
||||
|
||||
if (!currentEmber.XformCount())//Ensure there is always at least one xform or else the renderer will crash when trying to render.
|
||||
@ -532,8 +496,6 @@ private:
|
||||
currentEmber.AddXform(xform);
|
||||
}
|
||||
|
||||
//if (!Interpolater<T>::InterpMissingColors(currentEmber.m_Palette.m_Entries))
|
||||
// AddToReport(string(loc) + " : Error interpolating missing palette colors");
|
||||
currentEmber.CacheXforms();
|
||||
currentEmber.m_Index = embers.size();
|
||||
currentEmber.m_ParentFilename = parentFileString;
|
||||
@ -562,7 +524,6 @@ private:
|
||||
const char* loc = __FUNCTION__;
|
||||
int soloXform = -1;
|
||||
size_t i, count = 0, index = 0;
|
||||
double vals[16];
|
||||
xmlAttrPtr att, curAtt;
|
||||
xmlNodePtr editNode, childNode, motionNode;
|
||||
currentEmber.m_Palette.Clear();//Wipe out the current palette.
|
||||
@ -655,28 +616,16 @@ private:
|
||||
}
|
||||
else if (!Compare(curAtt->name, "size"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lu %lu", ¤tEmber.m_FinalRasW, ¤tEmber.m_FinalRasH) != 2)
|
||||
{
|
||||
AddToReport(string(loc) + " : Invalid size attribute " + string(attStr));
|
||||
//Assign reasonable defaults.
|
||||
currentEmber.m_FinalRasW = 1000;
|
||||
currentEmber.m_FinalRasH = 1000;
|
||||
}
|
||||
|
||||
istringstream is(attStr);
|
||||
is >> currentEmber.m_FinalRasW >> currentEmber.m_FinalRasH;
|
||||
currentEmber.m_OrigFinalRasW = currentEmber.m_FinalRasW;
|
||||
currentEmber.m_OrigFinalRasH = currentEmber.m_FinalRasH;
|
||||
}
|
||||
else if (!Compare(curAtt->name, "center"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf %lf", &vals[0], &vals[1]) != 2)
|
||||
{
|
||||
AddToReport(string(loc) + " : Invalid center attribute " + string(attStr));
|
||||
vals[0] = 0;
|
||||
vals[1] = 0;
|
||||
}
|
||||
|
||||
currentEmber.m_CenterX = T(vals[0]);
|
||||
currentEmber.m_CenterY = currentEmber.m_RotCenterY = T(vals[1]);
|
||||
istringstream is(attStr);
|
||||
is >> currentEmber.m_CenterX >> currentEmber.m_CenterY;
|
||||
currentEmber.m_RotCenterY = currentEmber.m_CenterY;
|
||||
}
|
||||
else if (!Compare(curAtt->name, "filter_shape"))
|
||||
{
|
||||
@ -700,29 +649,22 @@ private:
|
||||
}
|
||||
else if (!Compare(curAtt->name, "background"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf %lf %lf", &vals[0], &vals[1], &vals[2]) != 3)
|
||||
{
|
||||
AddToReport(string(loc) + " : Invalid background attribute " + string(attStr));
|
||||
vals[0] = 0;
|
||||
vals[1] = 0;
|
||||
vals[2] = 0;
|
||||
}
|
||||
|
||||
currentEmber.m_Background[0] = T(vals[0]);//[0..1]
|
||||
currentEmber.m_Background[1] = T(vals[1]);
|
||||
currentEmber.m_Background[2] = T(vals[2]);
|
||||
istringstream is(attStr);
|
||||
is >> currentEmber.m_Background[0]//[0..1]
|
||||
>> currentEmber.m_Background[1]
|
||||
>> currentEmber.m_Background[2];
|
||||
}
|
||||
else if (!Compare(curAtt->name, "curves"))
|
||||
{
|
||||
stringstream ss(attStr);
|
||||
istringstream is(attStr);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
for (glm::length_t j = 0; j < 4; j++)
|
||||
{
|
||||
ss >> currentEmber.m_Curves.m_Points[i][j].x;
|
||||
ss >> currentEmber.m_Curves.m_Points[i][j].y;
|
||||
ss >> currentEmber.m_Curves.m_Weights[i][j];
|
||||
is >> currentEmber.m_Curves.m_Points[i][j].x
|
||||
>> currentEmber.m_Curves.m_Points[i][j].y
|
||||
>> currentEmber.m_Curves.m_Weights[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -748,36 +690,24 @@ private:
|
||||
|
||||
for (curAtt = att; curAtt; curAtt = curAtt->next)
|
||||
{
|
||||
attStr = reinterpret_cast<char*>(xmlGetProp(childNode, curAtt->name));
|
||||
a = 255;
|
||||
attStr = reinterpret_cast<char*>(xmlGetProp(childNode, curAtt->name));
|
||||
istringstream is(attStr);
|
||||
//This signifies that a palette is not being retrieved from the palette file, rather it's being parsed directly out of the ember xml.
|
||||
//This also means the palette has already been hue adjusted and it doesn't need to be done again, which would be necessary if it were
|
||||
//coming from the palette file.
|
||||
currentEmber.m_Palette.m_Index = -1;
|
||||
|
||||
if (!Compare(curAtt->name, "index"))
|
||||
{
|
||||
Aton(attStr, index);
|
||||
}
|
||||
else if (!Compare(curAtt->name, "rgb"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf %lf %lf", &r, &g, &b) != 3)
|
||||
AddToReport(string(loc) + " : Invalid rgb attribute " + string(attStr));
|
||||
}
|
||||
is >> r >> g >> b;
|
||||
else if (!Compare(curAtt->name, "rgba"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf %lf %lf %lf", &r, &g, &b, &a) != 4)
|
||||
AddToReport(string(loc) + " : Invalid rgba attribute " + string(attStr));
|
||||
}
|
||||
is >> r >> g >> b >> a;
|
||||
else if (!Compare(curAtt->name, "a"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf", &a) != 1)
|
||||
AddToReport(string(loc) + " : Invalid a attribute " + string(attStr));
|
||||
}
|
||||
is >> a;
|
||||
else
|
||||
{
|
||||
AddToReport(string(loc) + " : Unknown color attribute " + string(CCX(curAtt->name)));
|
||||
}
|
||||
|
||||
xmlFree(attStr);
|
||||
}
|
||||
@ -1051,13 +981,9 @@ private:
|
||||
ret = ret && AttToEmberMotionFloat(att, attStr, eEmberMotionParam::FLAME_MOTION_VIBRANCY, motion);
|
||||
else if (!Compare(curAtt->name, "background"))
|
||||
{
|
||||
double r, g, b;
|
||||
|
||||
if (sscanf_s(attStr, "%lf %lf %lf", &r, &g, &b) != 3)
|
||||
{
|
||||
AddToReport(string(loc) + " : Invalid flame motion background attribute " + string(attStr));
|
||||
r = g = b = 0;
|
||||
}
|
||||
double r = 0, g = 0, b = 0;
|
||||
istringstream is(attStr);
|
||||
is >> r >> g >> b;
|
||||
|
||||
if (r != 0)
|
||||
motion.m_MotionParams.push_back(MotionParam<T>(eEmberMotionParam::FLAME_MOTION_BACKGROUND_R, T(r)));
|
||||
@ -1070,13 +996,9 @@ private:
|
||||
}
|
||||
else if (!Compare(curAtt->name, "center"))
|
||||
{
|
||||
double cx, cy;
|
||||
|
||||
if (sscanf_s(attStr, "%lf %lf", &cx, &cy) != 2)
|
||||
{
|
||||
AddToReport(string(loc) + " : Invalid flame motion center attribute " + string(attStr));
|
||||
cx = cy = 0;
|
||||
}
|
||||
double cx = 0, cy = 0;
|
||||
istringstream is(attStr);
|
||||
is >> cx >> cy;
|
||||
|
||||
if (cx != 0)
|
||||
motion.m_MotionParams.push_back(MotionParam<T>(eEmberMotionParam::FLAME_MOTION_CENTER_X, T(cx)));
|
||||
@ -1148,7 +1070,6 @@ private:
|
||||
size_t j;
|
||||
T temp;
|
||||
double a, b, c, d, e, f;
|
||||
double vals[10];
|
||||
xmlAttrPtr attPtr, curAtt;
|
||||
//Loop through the attributes of the xform element.
|
||||
attPtr = childNode->properties;
|
||||
@ -1203,30 +1124,17 @@ private:
|
||||
}
|
||||
else if (!Compare(curAtt->name, "color"))
|
||||
{
|
||||
xform.m_ColorX = xform.m_ColorY = 0;
|
||||
|
||||
//Try two coords first .
|
||||
if (sscanf_s(attStr, "%lf %lf", &vals[0], &vals[1]) == 2)
|
||||
{
|
||||
xform.m_ColorX = T(vals[0]);
|
||||
xform.m_ColorY = T(vals[1]);
|
||||
}
|
||||
else if (sscanf_s(attStr, "%lf", &vals[0]) == 1)//Try one color.
|
||||
{
|
||||
xform.m_ColorX = T(vals[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
xform.m_ColorX = xform.m_ColorY = T(0.5);
|
||||
AddToReport(string(loc) + " : Malformed xform color attribute " + string(attStr) + ", using 0.5, 0.5");
|
||||
}
|
||||
istringstream is(attStr);
|
||||
xform.m_ColorX = xform.m_ColorY = T(0.5);
|
||||
is >> xform.m_ColorX;
|
||||
is >> xform.m_ColorY;//Very unlikely to be present, but leave for future use.
|
||||
}
|
||||
else if (!Compare(curAtt->name, "chaos"))
|
||||
{
|
||||
stringstream ss(attStr);
|
||||
istringstream is(attStr);
|
||||
j = 0;
|
||||
|
||||
while (ss >> temp)
|
||||
while (is >> temp)
|
||||
{
|
||||
xform.SetXaos(j, temp);
|
||||
j++;
|
||||
@ -1243,12 +1151,9 @@ private:
|
||||
}
|
||||
else if (!Compare(curAtt->name, "coefs"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf %lf %lf %lf %lf %lf", &a, &d, &b, &e, &c, &f) != 6)//Original did a complicated parsing scheme. This is easier.//ORIG
|
||||
{
|
||||
a = d = b = e = c = f = 0;
|
||||
AddToReport(string(loc) + " : Bad coeffs attribute " + string(attStr));
|
||||
}
|
||||
|
||||
istringstream is(attStr);
|
||||
a = b = c = d = e = f = 0;
|
||||
is >> a >> d >> b >> e >> c >> f;
|
||||
xform.m_Affine.A(T(a));
|
||||
xform.m_Affine.B(T(b));
|
||||
xform.m_Affine.C(T(c));
|
||||
@ -1258,12 +1163,9 @@ private:
|
||||
}
|
||||
else if (!Compare(curAtt->name, "post"))
|
||||
{
|
||||
if (sscanf_s(attStr, "%lf %lf %lf %lf %lf %lf", &a, &d, &b, &e, &c, &f) != 6)//Original did a complicated parsing scheme. This is easier.//ORIG
|
||||
{
|
||||
a = d = b = e = c = f = 0;
|
||||
AddToReport(string(loc) + " : Bad post coeffs attribute " + string(attStr));
|
||||
}
|
||||
|
||||
istringstream is(attStr);
|
||||
a = b = c = d = e = f = 0;
|
||||
is >> a >> d >> b >> e >> c >> f;
|
||||
xform.m_Post.A(T(a));
|
||||
xform.m_Post.B(T(b));
|
||||
xform.m_Post.C(T(c));
|
||||
@ -1469,64 +1371,33 @@ private:
|
||||
/// <returns>True if there were no errors, else false.</returns>
|
||||
bool ParseHexColors(char* colstr, Ember<T>& ember, size_t numColors, intmax_t chan)
|
||||
{
|
||||
size_t colorIndex = 0;
|
||||
size_t colorCount = 0;
|
||||
uint r, g, b, a;
|
||||
int ret;
|
||||
char tmps[2];
|
||||
size_t skip = std::abs(chan);
|
||||
bool ok = true;
|
||||
const char* loc = __FUNCTION__;
|
||||
stringstream ss, temp(colstr); ss >> std::hex;
|
||||
string s1, s;
|
||||
size_t tmp, colorCount = 0, colorIndex = 0;
|
||||
s.reserve(1536);
|
||||
|
||||
//Strip whitespace prior to first color.
|
||||
while (isspace(static_cast<int>(colstr[colorIndex])))
|
||||
colorIndex++;
|
||||
while (temp >> s1)
|
||||
s += s1;
|
||||
|
||||
do
|
||||
auto length = s.size();
|
||||
|
||||
for (size_t strIndex = 0; strIndex < length;)
|
||||
{
|
||||
//Parse an RGB triplet at a time.
|
||||
if (chan == 3)
|
||||
ret = sscanf_s(&(colstr[colorIndex]), "%2x%2x%2x", &r, &g, &b);
|
||||
else if (chan == -4)
|
||||
ret = sscanf_s(&(colstr[colorIndex]), "00%2x%2x%2x", &r, &g, &b);
|
||||
else // chan==4
|
||||
ret = sscanf_s(&(colstr[colorIndex]), "%2x%2x%2x%2x", &r, &g, &b, &a);
|
||||
|
||||
a = 1;//Original allows for alpha, even though it will most likely never happen. Ember omits support for it.
|
||||
|
||||
if ((chan != 4 && ret != 3) || (chan == 4 && ret != 4))
|
||||
for (glm::length_t i = 0; i < 3 && colorCount < ember.m_Palette.Size(); i++)
|
||||
{
|
||||
ok = false;
|
||||
r = g = b = 0;
|
||||
AddToReport(string(loc) + " : Problem reading hexadecimal color data, assigning to 0");
|
||||
break;
|
||||
const char tmpStr[3] = { s[strIndex++], s[strIndex++], 0 };//Read out and convert the string two characters at a time.
|
||||
ss.clear();//Reset and fill the string stream.
|
||||
ss.str(tmpStr);
|
||||
ss >> tmp;//Do the conversion.
|
||||
ember.m_Palette.m_Entries[colorCount][i] = T(tmp) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
}
|
||||
|
||||
colorIndex += 2 * skip;
|
||||
|
||||
while (isspace(static_cast<int>(colstr[colorIndex])))
|
||||
colorIndex++;
|
||||
|
||||
ember.m_Palette.m_Entries[colorCount].r = T(r) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
ember.m_Palette.m_Entries[colorCount].g = T(g) / T(255);
|
||||
ember.m_Palette.m_Entries[colorCount].b = T(b) / T(255);
|
||||
ember.m_Palette.m_Entries[colorCount].a = T(a);
|
||||
ember.m_Palette.m_Entries[colorCount][3] = T(1);
|
||||
colorCount++;
|
||||
}
|
||||
while (colorCount < numColors && colorCount < ember.m_Palette.m_Entries.size());
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
if (sscanf_s(&(colstr[colorIndex]), "%1s", tmps, sizeof(tmps)) > 0) //Really need to migrate all of this parsing to C++.//TODO
|
||||
#else
|
||||
if (sscanf_s(&(colstr[colorIndex]), "%1s", tmps) > 0)
|
||||
#endif
|
||||
{
|
||||
AddToReport(string(loc) + " : Extra data at end of hex color data " + string(&(colstr[colorIndex])));
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
return length >= 256;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Reference in New Issue
Block a user