1417 lines
43 KiB
C++
1417 lines
43 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
#include "GEO/GEO.h"
|
|
|
|
#if WITH_EDITOR
|
|
|
|
#include "GEO/IFileStream.h"
|
|
#include "HAL/PlatformFile.h"
|
|
#include "HAL/PlatformFileManager.h"
|
|
|
|
#ifdef USE_ZLIB
|
|
#include "ChaosFlesh/ZIP.h"
|
|
#endif
|
|
|
|
#include "Containers/StringConv.h"
|
|
|
|
#include <algorithm>
|
|
#include <charconv>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <memory>
|
|
|
|
namespace ChaosFlesh {
|
|
|
|
using namespace std;
|
|
|
|
//! Reads "abc 123", consuming quotes but returns unquoted.
|
|
//! \p delim denotes the character to read to (and is discarded)
|
|
//! \p bracketed specifies if the string is deliminated at the beginning and the end.
|
|
bool
|
|
Read(std::istream &inref, std::string &buffer, const char delim='"', const bool bracketed=true)
|
|
{
|
|
buffer.clear();
|
|
bool open=!bracketed;
|
|
while(inref.good())
|
|
{
|
|
char ch;
|
|
inref.get(ch); // consume next char
|
|
if(ch == delim)
|
|
{
|
|
if(!open)
|
|
open = true;
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
buffer.append(1, ch);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//! Given "[...]..." returns "[...]". Given "[...[...]...]..." returns "[...[...]...]".
|
|
bool
|
|
ReadMatching(
|
|
std::istream &inref,
|
|
std::string &buffer,
|
|
const char openDelim='[',
|
|
const char closeDelim=']',
|
|
bool stripBrackets=false)
|
|
{
|
|
buffer.clear();
|
|
int open = 0;
|
|
inref >> std::ws; // discard white space
|
|
bool inQuote = false;
|
|
const bool sameChar = openDelim == closeDelim;
|
|
while(inref.good())
|
|
{
|
|
if(!inQuote) // if not currently parsing a quoted string...
|
|
inref >> std::ws; // discard white space
|
|
|
|
char ch;
|
|
inref.get(ch); // consume next char
|
|
|
|
if(sameChar)
|
|
{
|
|
if(ch == openDelim)
|
|
{
|
|
if(!open)
|
|
{
|
|
open++;
|
|
if(!stripBrackets) buffer.append(1, ch);
|
|
}
|
|
else
|
|
{
|
|
open--;
|
|
if(!stripBrackets) buffer.append(1, ch);
|
|
}
|
|
}
|
|
else
|
|
buffer.append(1, ch);
|
|
}
|
|
else
|
|
{
|
|
if(ch == openDelim)
|
|
{
|
|
open++;
|
|
if(!stripBrackets || (stripBrackets && open > 1)) buffer.append(1, ch);
|
|
}
|
|
else if(ch == closeDelim)
|
|
{
|
|
if(!stripBrackets || (stripBrackets && open > 1)) buffer.append(1, ch);
|
|
open--;
|
|
}
|
|
else
|
|
buffer.append(1, ch);
|
|
}
|
|
|
|
if(ch == '"')
|
|
inQuote=!inQuote;
|
|
if(!open)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//! Read numeric characters from \p inref storing them in \p buffer, until a
|
|
//! non-numeric character is encountered. Doesn't try to parse or validate.
|
|
bool
|
|
ReadNumeric(std::istream &inref, std::string &buffer)
|
|
{
|
|
buffer.clear();
|
|
inref >> std::ws; // discard white space
|
|
while(inref.good())
|
|
{
|
|
inref >> std::ws; // discard white space
|
|
const char nch = inref.peek();
|
|
switch(nch)
|
|
{
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
|
|
case 'e':
|
|
case 'E':
|
|
|
|
case '.':
|
|
case '-':
|
|
buffer.append(1, nch);
|
|
inref.ignore();
|
|
break;
|
|
default:
|
|
return !buffer.empty();
|
|
}
|
|
}
|
|
return !buffer.empty();
|
|
}
|
|
|
|
std::string
|
|
ParseString(const std::string& str)
|
|
{
|
|
if (str.empty())
|
|
return str;
|
|
if (str.substr(0, 2) == "\\\"" && str.substr(str.size() - 2, 2) == "\\\"")
|
|
return str.substr(2, str.size() - 4);
|
|
else if (str.front() == '"' && str.back() == '"')
|
|
return str.substr(1, str.size() - 2);
|
|
return str;
|
|
}
|
|
|
|
bool
|
|
ParseGeoKVPairs(
|
|
std::istream &inref,
|
|
TMap<FString, std::string> &kvp,
|
|
const char *filename=nullptr,
|
|
std::ostream *errorStream=nullptr,
|
|
TArray<std::string> *allValues=nullptr)
|
|
{
|
|
std::string key;
|
|
key.reserve(128);
|
|
std::string buffer;
|
|
buffer.reserve(2048);
|
|
|
|
bool globalOpenBracket = false;
|
|
while(inref.good())
|
|
{
|
|
inref >> std::ws; // discard white space
|
|
|
|
//
|
|
// Read key
|
|
//
|
|
|
|
if(key.empty())
|
|
{
|
|
const char nch = inref.peek();
|
|
switch(nch)
|
|
{
|
|
case '[': // document and section (nv pair list)
|
|
case '{': // dictionary parsing
|
|
// Aside from the global opening bracket, we should not encounter brackets on the key side.
|
|
if(globalOpenBracket)
|
|
goto error;
|
|
globalOpenBracket=true;
|
|
inref.ignore(); // Skip this char
|
|
continue; // Try again for key
|
|
case ']':
|
|
case '}':
|
|
// Closing bracket must match the opening bracket.
|
|
if(!globalOpenBracket)
|
|
goto error;
|
|
globalOpenBracket=false;
|
|
inref.ignore(); // Skip this char
|
|
break; // Stop parsing
|
|
case '"':
|
|
// Keys should be quoted strings.
|
|
if(!Read(inref, buffer))
|
|
goto error;
|
|
key = buffer;
|
|
break;
|
|
}
|
|
|
|
if(key.empty())
|
|
{
|
|
if(!globalOpenBracket)
|
|
{
|
|
// done reading
|
|
break;
|
|
}
|
|
if(errorStream)
|
|
*errorStream << "Failed to determine key at file position: " << inref.tellg() << "." << std::endl;
|
|
goto error;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Read value
|
|
//
|
|
|
|
buffer.clear();
|
|
const char nch = inref.peek();
|
|
switch(nch)
|
|
{
|
|
case ',': // "<key name>",
|
|
case ':': // "<key name>":
|
|
inref.ignore(); // Skip this char
|
|
continue; // Try again for value
|
|
case '"': // "<key name>","
|
|
if(!Read(inref, buffer))
|
|
goto error;
|
|
break;
|
|
case '{': // "<key name>",{
|
|
if(!ReadMatching(inref, buffer, '{', '}'))
|
|
goto error;
|
|
break;
|
|
case '[': // "<key name>",[
|
|
if(!ReadMatching(inref, buffer, '[', ']'))
|
|
goto error;
|
|
break;
|
|
case 't': // "<key name>",true
|
|
buffer = "true";
|
|
inref.ignore(4);
|
|
if(!inref.good())
|
|
goto error;
|
|
break;
|
|
case 'f': // "<key name>",false
|
|
buffer = "false";
|
|
inref.ignore(5);
|
|
if(!inref.good())
|
|
goto error;
|
|
break;
|
|
default: // "<key name>",<numeric value>
|
|
if(!ReadNumeric(inref, buffer))
|
|
goto error;
|
|
break;
|
|
}
|
|
|
|
if(inref.peek()==',') // consume trailing ','
|
|
inref.ignore();
|
|
|
|
kvp.Add(FString(key.c_str()), buffer);
|
|
if (allValues)
|
|
{
|
|
allValues->Add(key);
|
|
allValues->Add(buffer);
|
|
}
|
|
key.clear();
|
|
buffer.clear();
|
|
} // end while(inref.good())
|
|
|
|
return true;
|
|
|
|
error:
|
|
if(errorStream && filename)
|
|
*errorStream << "ParseGeoKVPairs(): parse error in file '" << filename << "' at position: " << inref.tellg() << "." << std::endl;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ParseKVPairs(
|
|
const std::string &str,
|
|
TMap<FString, std::string> &kvp,
|
|
const char *filename=nullptr,
|
|
std::ostream *errorStream=nullptr,
|
|
TArray<std::string>* allValues=nullptr)
|
|
{
|
|
std::stringstream ss(str);
|
|
return ParseGeoKVPairs(ss, kvp, filename, errorStream, allValues);
|
|
}
|
|
|
|
std::string
|
|
FormatStringArray(const TArray<std::string> &values, const char openBracket='[', const char closeBracket=']')
|
|
{
|
|
std::string retval;
|
|
retval += openBracket;
|
|
for(size_t i=0; i < values.Num(); i++)
|
|
{
|
|
if(i > 0) retval += ",";
|
|
retval += '"' + values[i] + '"';
|
|
}
|
|
retval += closeBracket;
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
//! Prints \p kvp by sorted keys. Values longer than \p maxLen are truncated in the middle: "abcd<...>wxyz".
|
|
void
|
|
DebugPrint(const TMap<FString,std::string> &kvp, const std::string &prefix=" ", const size_t maxLen=128)
|
|
{
|
|
//std::vector<std::string> keys;
|
|
//for(auto it=kvp.begin(), itEnd=kvp.end(); it != itEnd; ++it)
|
|
// keys.push_back(it->first);
|
|
//std::sort(keys.begin(), keys.end());
|
|
TArray<FString> keys;
|
|
kvp.GetKeys(keys);
|
|
keys.Sort();
|
|
|
|
for(const auto &key : keys)
|
|
{
|
|
//const std::string &value = kvp.find(key)->second;
|
|
const std::string& value = *kvp.Find(key);
|
|
if(value.size() > maxLen)
|
|
{
|
|
std::cout << prefix
|
|
<< key << " = "
|
|
<< value.substr(0,maxLen/2) << "<...>"
|
|
<< value.substr(value.size()-(maxLen/2)) << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << prefix
|
|
<< key << " = " << value << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DebugPrint(const TMap<FString,TMap<FString,std::string>> &kvp, const std::string &prefix=" ")
|
|
{
|
|
for(auto it=kvp.begin(), itEnd=kvp.end(); it != itEnd; ++it)
|
|
DebugPrint(it->second, prefix+'.'+it->first+'.');
|
|
}
|
|
*/
|
|
|
|
//! Array format:
|
|
//! [ [...],[...],... ]
|
|
bool
|
|
ParseArray(const std::string &strIn, TArray<std::string> &values, const char openBracket='[', const char closeBracket=']', const bool stripInternalBrackets=false)
|
|
{
|
|
values.Empty();
|
|
bool open = false;
|
|
std::string buffer;
|
|
|
|
std::stringstream ss(strIn);
|
|
ss >> std::ws; // discard white space
|
|
|
|
while(ss.good())
|
|
{
|
|
ss >> std::ws; // discard white space
|
|
char ch = ss.peek();
|
|
|
|
if(ch == openBracket)
|
|
{
|
|
if(!open)
|
|
{
|
|
ss.ignore(); // consume this char
|
|
open = true;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if(ReadMatching(ss, buffer, openBracket, closeBracket, stripInternalBrackets))
|
|
values.Add(buffer);
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
else if(ch == '"') // array of strings
|
|
{
|
|
if(ReadMatching(ss, buffer, '"', '"', stripInternalBrackets))
|
|
values.Add(buffer);
|
|
else
|
|
return false;
|
|
}
|
|
else if (isdigit(ch))
|
|
{
|
|
buffer.resize(0);
|
|
do {
|
|
buffer.push_back(ch);
|
|
ss.ignore(); // consume this char
|
|
if (!ss.good())
|
|
break;
|
|
ch = ss.peek();
|
|
} while (isdigit(ch));
|
|
values.Add(buffer);
|
|
}
|
|
else if(ch == ',')
|
|
{
|
|
ss.ignore(); // consume this char
|
|
continue;
|
|
}
|
|
else if(ch == closeBracket)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "ParseArray() - parse failure at position " << ss.tellg()
|
|
<< ", expected '[],', got '" << ch << "', "
|
|
<< " input string: '" << strIn << "'" << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if defined(__GNUC__)
|
|
// The gnu stdlib has issues with the floating point versions of std::from_chars() (their
|
|
// implementations allocate memory, which the standard forbids), and they hide them behind
|
|
// a macro through v20. Lame. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100146
|
|
bool
|
|
ParseNum(const std::string &strIn, double &value)
|
|
{
|
|
const char* str = strIn.c_str();
|
|
char* strEnd = nullptr;
|
|
value = std::strtod(str, &strEnd);
|
|
if(value == 0. && strEnd == str)
|
|
return false;
|
|
return true;
|
|
}
|
|
bool
|
|
ParseNum(const std::string &strIn, float &value)
|
|
{
|
|
const char* str = strIn.c_str();
|
|
char* strEnd = nullptr;
|
|
value = std::strtof(str, &strEnd);
|
|
if(value == 0. && strEnd == str)
|
|
return false;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
//! Parse a numeric value of type \p T from \p strIn. Returns \c false on error.
|
|
template <class T>
|
|
#if defined(__GNUC__)
|
|
// The gnu stdlib has issues with the floating point versions of std::from_chars() (their
|
|
// implementations allocate memory, which the standard forbids), and they hide them behind
|
|
// a macro through v20. Lame. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100146
|
|
//
|
|
// Enable if T is integral on gnu.
|
|
typename std::enable_if<std::is_integral<T>::value, bool>::type
|
|
#else
|
|
bool
|
|
#endif
|
|
ParseNum(const std::string &strIn, T &value)
|
|
{
|
|
|
|
//if(std::from_chars(&strIn[0], &strIn[0]+strIn.size(), value).ec != std::errc())
|
|
if(std::from_chars(strIn.c_str(), strIn.c_str()+strIn.size(), value).ec != std::errc())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
//! Parses numeric values of type \p T from \p strIn in array format demarked by
|
|
//! \p openBracket and \p closeBracket, with values delinated by commas.
|
|
//! <code>
|
|
//! TArray<int32> values;
|
|
//! ParseNum("[1, 2, 3, 4, 5, 6, 7, 8, 9]", values);
|
|
//! <\code>
|
|
template <class T>
|
|
bool
|
|
ParseNumArray(const std::string &strIn, TArray<T> &values, const char openBracket='[', const char closeBracket=']', const bool debug=false)
|
|
{
|
|
//values.clear();
|
|
values.Empty();
|
|
|
|
// We're parsing an ascii format, and so there may be line breaks within str for formatting reasons.
|
|
// Also, std::from_chars() fails with leading white space. Just remove all ws.
|
|
std::string str; str.reserve(strIn.size());
|
|
size_t num = 0;
|
|
for(char ch : strIn)
|
|
{
|
|
if(!std::isspace(ch))
|
|
str.push_back(ch);
|
|
if(ch == ',')
|
|
num++;
|
|
}
|
|
values.Reserve(num+1);
|
|
|
|
size_t i = str.find_first_of(openBracket);
|
|
if(i == std::string::npos)
|
|
i = 0; // Didn't find openBracket, start at 0.
|
|
else
|
|
i++; // Found openBracket, skip it.
|
|
std::string delim(","); delim+=closeBracket; delim+=openBracket;
|
|
while(i < str.size())
|
|
{
|
|
// advance
|
|
i = str.find_first_not_of(delim, i);
|
|
if(i == std::string::npos)
|
|
break;
|
|
|
|
size_t j = i+1<str.size() ? str.find_first_of(delim, i+1) : str.size();
|
|
if(j == std::string::npos)
|
|
j = str.size(); // Didn't find closeBracket, point 1 past last char.
|
|
|
|
const size_t len = j-i;
|
|
|
|
values.SetNum(values.Num()+1);
|
|
if(!ParseNum(str.substr(i, len), values[values.Num()-1]))
|
|
{
|
|
if(debug)
|
|
std::cout << "ParseNumArray() - std::from_chars() failed on '" << str.substr(i, len)
|
|
<< "' in context [" << std::max(((int)i)-64,0) << ", " << std::min((int)str.size(),((int)i)+128) << "]: " << std::endl
|
|
<< "'" << str.substr(std::max(((int)i)-64,0), std::min((int)str.size(),((int)i)+128))
|
|
<< "', i: " << i << " j: " << j << " len: " << len << " - delim: '" << delim << "'."
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
i = j+1;
|
|
}
|
|
if(debug)
|
|
{
|
|
std::cout << "ParseNumArray() - strIn: '" << strIn.substr(0, 128) << "'" << std::endl;
|
|
for(int j=0; j < std::min((int)values.Num(),10); j++)
|
|
std::cout << values[j] << ", ";
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//! Returns \c true if \p strIn contains a array of valid numeric values.
|
|
bool
|
|
IsNumArray(const std::string &strIn, size_t &numValues, const char openBracket='[', const char closeBracket=']')
|
|
{
|
|
numValues = 0;
|
|
std::string str; str.reserve(128);
|
|
for(char ch : strIn)
|
|
{
|
|
if(std::isspace(ch))
|
|
continue;
|
|
// [71500,25743,43157,43152,82894,...]
|
|
// [[0,0,0],[-0.402781129,-0.911162317,0.0868939161],[-0.392075658,-0.91307807,0.112093881],...]
|
|
if(ch == openBracket || ch == closeBracket || ch == ',')
|
|
{
|
|
if(!str.empty())
|
|
{
|
|
double num;
|
|
if(!ParseNum(str, num)) // parse to double
|
|
return false;
|
|
numValues++;
|
|
}
|
|
str.clear();
|
|
}
|
|
else if(ch == '"')
|
|
return false;
|
|
else
|
|
str.push_back(ch);
|
|
}
|
|
return numValues != 0;
|
|
}
|
|
|
|
//! Parses numeric values of type \p T from \p strIn in array format demarked by
|
|
//! \p openBracket and \p closeBracket, with vector values delinated by commas.
|
|
//! <code>
|
|
//! TArray<int> values;
|
|
//! ParseVector3Array("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]", values);
|
|
//! <\code>
|
|
template <class T>
|
|
bool
|
|
ParseVector3Array(const std::string &strIn, TArray<T> &values, const char openBracket='[', const char closeBracket=']')
|
|
{
|
|
values.clear();
|
|
std::string substr; substr.reserve(1024);
|
|
TArray<T> tmp; tmp.Reserve(3);
|
|
|
|
size_t i = strIn.find(openBracket, 0);
|
|
size_t iEnd = strIn.find_last_of(closeBracket, 0);
|
|
if(i == std::string::npos || iEnd == std::string::npos)
|
|
return false;
|
|
while(i < iEnd)
|
|
{
|
|
i = strIn.find_first_of(openBracket, i+1);
|
|
if(i == std::string::npos)
|
|
break;
|
|
size_t j = strIn.find_first_of(closeBracket, i+1);
|
|
if(j == std::string::npos)
|
|
break;
|
|
|
|
substr = strIn.substr(i, j+1-i); // [i, j]
|
|
if(!ParseNumArray(substr, tmp, openBracket, closeBracket) || tmp.Num()!=3)
|
|
return false;
|
|
|
|
size_t curr = values.size();
|
|
values.SetNum(curr+tmp.Num());
|
|
for(size_t k=0; k < tmp.Num(); k++)
|
|
values[curr+k] = tmp[k];
|
|
|
|
i = j;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
IsVector3(const std::string &str)
|
|
{ TArray<float> vec; if(ParseNumArray(str, vec) && vec.Num()==3) return true; return false; }
|
|
|
|
bool
|
|
ParsePrimitives(
|
|
const std::string &str,
|
|
TMap<FString, std::string> &flattenedKvp)
|
|
{
|
|
/*
|
|
"primitives",
|
|
|
|
1 [
|
|
2 [
|
|
3 ["type","Tetrahedron_run"],["startvertex",0,"nprimitives",8895]
|
|
-2 ],
|
|
2 [
|
|
3 ["type","Polygon_run"],["startvertex",35580,"nprimitives",162,"nvertices_rle",[3,162]]
|
|
-2 ]
|
|
-1 ]
|
|
*/
|
|
|
|
std::string l1str;
|
|
{
|
|
std::stringstream ss(str);
|
|
if (!ReadMatching(ss, l1str, '[', ']', true)) // strip outer brackets 1
|
|
exit(1);
|
|
}
|
|
|
|
// Get L2 string
|
|
std::stringstream ss(l1str);
|
|
while(ss.good())
|
|
{
|
|
ss >> std::ws; // discard white space
|
|
if (!ss.good())
|
|
break;
|
|
char ch = ss.peek();
|
|
std::string l2str;
|
|
switch (ch)
|
|
{
|
|
case '[':
|
|
ReadMatching(ss, l2str, '[', ']', true); // strip outer brackets 2, advances ss
|
|
break;
|
|
case ',':
|
|
ss.get(ch); // consume char
|
|
break;
|
|
default:
|
|
//exit(1);
|
|
continue;
|
|
}
|
|
if (l2str.empty())
|
|
continue;
|
|
|
|
// Parse L3 arrays
|
|
std::string prefix;
|
|
std::stringstream l2ss(l2str);
|
|
while (l2ss.good())
|
|
{
|
|
l2ss >> std::ws; // discard white space
|
|
if (!l2ss.good())
|
|
break;
|
|
char l2ch = l2ss.peek();
|
|
std::string l3str;
|
|
switch (l2ch)
|
|
{
|
|
case '[':
|
|
ReadMatching(l2ss, l3str, '[', ']', false); // keep brackets, advances l2ss
|
|
break;
|
|
case ',':
|
|
l2ss.get(l2ch);
|
|
break;
|
|
default:
|
|
//exit(1);
|
|
continue;
|
|
}
|
|
if (l3str.empty())
|
|
continue;
|
|
|
|
TArray<std::string> values;
|
|
if (ParseArray(l3str, values))
|
|
{
|
|
for (int i = 0; i < values.Num() - 1; i++)
|
|
{
|
|
const std::string key = ParseString(values[i]);
|
|
const std::string value = ParseString(values[++i]);
|
|
if (key == "type")
|
|
prefix = value + ":";
|
|
else
|
|
{
|
|
std::string k = prefix + key;
|
|
flattenedKvp.Add(FString(k.c_str()), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ParseTopology(
|
|
const std::string &str,
|
|
TMap<FString, std::string> &flattenedKvp)
|
|
{
|
|
/*
|
|
"topology",[
|
|
"pointref",[
|
|
"indices",[71500,25743,43157,...,88117,89363,81072]
|
|
]
|
|
],
|
|
*/
|
|
TMap<FString, std::string> level1kvp;
|
|
if(!ParseKVPairs(str, level1kvp))
|
|
{
|
|
std::cout << "ParseTopology() - ParseKVPairs() failed at level 1 with str: '" << str << "'" << std::endl;
|
|
exit(1);
|
|
}
|
|
for(const auto & [l1key, l1value] : level1kvp)
|
|
{
|
|
TMap<FString, std::string> level2kvp;
|
|
if(!ParseKVPairs(l1value, level2kvp))
|
|
{
|
|
std::cout << "ParseTopology() - ParseKVPairs() failed at level 2 with str: '" << l1value << "'" << std::endl;
|
|
exit(1);
|
|
}
|
|
|
|
TMap<FString, std::string> level3kvp;
|
|
if(!ParseKVPairs(l1value, level3kvp))
|
|
{
|
|
std::cout << "ParseTopology() - ParseKVPairs() failed at level 3 with str: '" << l1value << "'" << std::endl;
|
|
exit(1);
|
|
}
|
|
|
|
for (const auto& [l3key, l3value] : level3kvp)
|
|
{
|
|
std::string tmp = std::string(TCHAR_TO_UTF8(*l1key)) + "." + std::string(TCHAR_TO_UTF8(*l3key));
|
|
flattenedKvp.Add(FString(tmp.c_str()), l3value);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ParseAttributes(
|
|
const std::string &str,
|
|
TMap<FString, TMap<FString, std::string>> &categorizedKvp)
|
|
{
|
|
// "vertexattributes",[...],
|
|
// "pointattributes",[...],
|
|
// "primitiveattributes",
|
|
TMap<FString, std::string> attrCategories;
|
|
ParseKVPairs(str, attrCategories);
|
|
for(auto it = attrCategories.CreateConstIterator(); it; ++it)
|
|
{
|
|
const FString& category = it.Key();
|
|
const std::string& catScope = it.Value();
|
|
TMap<FString, std::string> &kvp = categorizedKvp.Add(category);
|
|
|
|
TArray<std::string> attrNames;
|
|
|
|
TArray<std::string> attrTuples;
|
|
if(!ParseArray(catScope, attrTuples))
|
|
{
|
|
std::cout << "ParseAttributes() - failed to parse attr tuples." << std::endl;
|
|
return false;
|
|
}
|
|
for(const std::string &attrStr : attrTuples)
|
|
{
|
|
TArray<std::string> attrBlocks;
|
|
if(!ParseArray(attrStr, attrBlocks))
|
|
{
|
|
std::cout << "ParseAttributes() - failed to parse attr blocks from str: '"
|
|
<< (attrStr.size()>1024?attrStr.substr(0,1024):attrStr) << "'" << std::endl;
|
|
break;
|
|
}
|
|
|
|
std::string attrName;
|
|
for(const std::string &attrBlock : attrBlocks)
|
|
{
|
|
TMap<FString, std::string> attrVars;
|
|
if(!ParseKVPairs(attrBlock, attrVars))
|
|
{
|
|
std::cout << "ParseAttributes() - failed to parse attrVars from str: '" << attrStr << "'" << std::endl;
|
|
break;
|
|
}
|
|
// Attribute name should be in the first block
|
|
if(attrName.empty())
|
|
{
|
|
const auto nameIt=attrVars.Find("name");
|
|
if(nameIt == nullptr)
|
|
{
|
|
std::cout << "ParseAttributes() - failed to find attr name!" << std::endl;
|
|
break;
|
|
}
|
|
attrName = *nameIt;
|
|
attrNames.Add(attrName);
|
|
}
|
|
//std::string attrPath = "attributes." + category + "." + attrName + ".";
|
|
std::string attrPath = attrName + ".";
|
|
for(const auto & [k, v] : attrVars)
|
|
{
|
|
if(k == "values" || k == "indices")
|
|
{
|
|
TMap<FString, std::string> valuesKvp;
|
|
if(!ParseKVPairs(v, valuesKvp))
|
|
{
|
|
std::cout << "ParseAttributes() - failed to parse values for attr: '" << std::string(TCHAR_TO_UTF8(*k))
|
|
<< "' from str: '" << v << "'" << std::endl;
|
|
break;
|
|
}
|
|
for(const auto & [valuesK, valuesV] : valuesKvp)
|
|
{
|
|
if(valuesK == "arrays" && valuesV.substr(0,2) == "[[")
|
|
{
|
|
// Strip off extra brackets from "arrays" entries.
|
|
std::string tmp = attrPath + std::string(TCHAR_TO_UTF8(*k)) + '.' + std::string(TCHAR_TO_UTF8(*valuesK));
|
|
kvp.Add(FString(tmp.c_str()), valuesV.substr(1, valuesV.size() - 2));
|
|
}
|
|
else
|
|
{
|
|
std::string tmp = attrPath + std::string(TCHAR_TO_UTF8(*k)) + '.' + std::string(TCHAR_TO_UTF8(*valuesK));
|
|
kvp.Add(FString(tmp.c_str()), valuesV);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::string tmp = attrPath + std::string(TCHAR_TO_UTF8(*k));
|
|
kvp.Add(FString(tmp.c_str()), v);
|
|
}
|
|
}
|
|
} // end for attrBlocks
|
|
} // end for attrTuples
|
|
|
|
kvp.Add("entries", FormatStringArray(attrNames));
|
|
} // end for categories
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FlattenDictionaries(
|
|
TMap<FString, std::string> &kvpIn,
|
|
TMap<FString, std::string> &kvpOut)
|
|
{
|
|
kvpOut.Empty();
|
|
for(auto it=kvpIn.CreateIterator(); it; ++it)
|
|
{
|
|
const FString& key = it.Key();
|
|
std::string &value = it.Value();
|
|
if(key.IsEmpty() || value.empty())
|
|
continue;
|
|
|
|
if(value[0] == '{')
|
|
{
|
|
TMap<FString,std::string> kvp;
|
|
if(!ParseKVPairs(value, kvp))
|
|
{
|
|
std::cout << "FlattenKVPairs() - ParseKVPairs returned false on key '" << std::string(TCHAR_TO_UTF8(*key)) << "' value: '" << value << "'" << std::endl;
|
|
continue;
|
|
}
|
|
for (auto& [l2k, l2v] : kvp)
|
|
{
|
|
std::string tmp = std::string(TCHAR_TO_UTF8(*key)) + '.' + std::string(TCHAR_TO_UTF8(*l2k));
|
|
kvpOut.Add(FString(tmp.c_str()), l2v);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kvpOut.Add(key, value);
|
|
}
|
|
}
|
|
kvpIn.Empty();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ProcessInfo(
|
|
TMap<FString, int32>& IntVars,
|
|
const TMap<FString, std::string> &kvp)
|
|
{
|
|
int count=0;
|
|
auto it=kvp.Find("pointcount");
|
|
if(it != nullptr)
|
|
{
|
|
const std::string &value = *it;
|
|
if(ParseNum(value, count) && count > 0)
|
|
{
|
|
IntVars.Add("pointcount", count);
|
|
}
|
|
}
|
|
/*
|
|
if(!count)
|
|
{
|
|
//it=kvp.find("pointcount");
|
|
it=kvp.find("vertexcount");
|
|
if(it != itEnd)
|
|
{
|
|
const std::string &value = it->second;
|
|
if(ParseNum(value, count) && count > 0)
|
|
{
|
|
pdm->addParticles(count);
|
|
kvp.erase(it);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
/*
|
|
for(it=kvp.begin(), itEnd=kvp.end(); it != itEnd; ++it)
|
|
{
|
|
const std::string &key = it->first;
|
|
const std::string &value = it->second;
|
|
|
|
FixedAttribute attr = pdm->addFixedAttribute(key.c_str(), ParticleAttributeType::INDEXEDSTR, 1);
|
|
const int id = pdm->registerFixedIndexedStr(attr, value.c_str());
|
|
pdm->setFixed(attr, &id);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* IntVars:
|
|
* { ["Tetrahedron_run:startvertex",0], ["Tetrahedron_run:nprimitives",8895],
|
|
* ["Polygon_run:startvertex",35580], ["Polygon_run:nprimitives", 162] }
|
|
*/
|
|
void
|
|
ProcessPrimitivesAndTopology(
|
|
TMap<FString, int32>& IntVars,
|
|
TMap<FString, TArray<int32>>& IntVectorVars,
|
|
const TMap<FString, std::string> &prim,
|
|
const TMap<FString, std::string> &topo)
|
|
{
|
|
// primitives:
|
|
std::string type;
|
|
for(auto it=prim.CreateConstIterator(); it; ++it)
|
|
{
|
|
const FString key=it.Key();
|
|
const std::string value=it.Value();
|
|
|
|
int vi;
|
|
if (ParseNum(value, vi))
|
|
{
|
|
std::string tmp = std::string(TCHAR_TO_UTF8(*key));
|
|
IntVars.Add(FString(tmp.c_str()), vi);
|
|
}
|
|
}
|
|
|
|
// topo:
|
|
// pointref.indices = int array
|
|
for(auto it=topo.CreateConstIterator(); it; ++it)
|
|
{
|
|
const FString key=it.Key();
|
|
const std::string value=it.Value();
|
|
|
|
TArray<int> iv;
|
|
if(ParseNumArray(value, iv))
|
|
{
|
|
IntVectorVars.Add(key, iv);
|
|
}
|
|
else
|
|
{
|
|
//UE_LOG(LogChaosFlesh, Display, TEXT("Unsupported topology attribute '%s' = '%s'."),
|
|
// *key,
|
|
// (value.size() > 128 ? value.substr(0, 64) + "<...>" + value.substr(value.size() - 64) : value).c_str());
|
|
std::cerr << "Unsupported topology attribute '" << std::string(TCHAR_TO_UTF8(*key))
|
|
<< "' = '" << (value.size()>128?value.substr(0,64)+"<...>"+value.substr(value.size()-64):value)
|
|
<< "'." << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ProcessAttributes(
|
|
TMap<FString, int32>& IntVars,
|
|
TMap<FString, TArray<int32>>& IntVectorVars,
|
|
TMap<FString, TArray<float>>& FloatVectorVars,
|
|
TMap<FString, TPair<TArray<std::string>, TArray<int32>>>& IndexedStringVars,
|
|
const TMap<FString, TMap<FString, std::string>> &catToAttrs)
|
|
{
|
|
for(auto cIt=catToAttrs.CreateConstIterator(); cIt; ++cIt)
|
|
{
|
|
const FString &category = cIt.Key(); // vertexattributes, pointattributes, primitiveattributes
|
|
const auto &attrs = cIt.Value();
|
|
|
|
auto it = attrs.Find("entries");
|
|
if(it == nullptr)
|
|
continue;
|
|
const std::string &attrNamesStr = *it;
|
|
TArray<std::string> attrNames;
|
|
ParseArray(attrNamesStr, attrNames, '[', ']', true);
|
|
|
|
//for(auto nIt=attrNames.begin(), nItEnd=attrNames.end(); nIt != nItEnd; ++nIt)
|
|
for(const std::string &name : attrNames)
|
|
{
|
|
//const std::string &name = *nIt;
|
|
|
|
it = attrs.Find(FString(std::string(name+".type").c_str()));
|
|
std::string type = it != nullptr ? *it : "";
|
|
if(type == "numeric")
|
|
{
|
|
it = attrs.Find(FString(std::string(name+".values.size").c_str()));
|
|
std::string sizeStr = it != nullptr ? *it : "";
|
|
int size=0;
|
|
if(!ParseNum(sizeStr, size))
|
|
{
|
|
std::cout << "ProcessAttributes() - failed to parse size string '" << sizeStr
|
|
<< "' for attribute: '" << std::string(TCHAR_TO_UTF8(*category)) << "." << name << "'." << std::endl;
|
|
continue;
|
|
}
|
|
std::string dataAttrName = size==1 ? name+".values.arrays" : name+".values.tuples";
|
|
it = attrs.Find(FString(dataAttrName.c_str()));
|
|
const std::string &dataStr = it != nullptr ? *it : "";
|
|
|
|
it = attrs.Find(FString(std::string(name+".values.storage").c_str()));
|
|
std::string storage = it != nullptr ? *it : "";
|
|
if(storage == "int32")
|
|
{
|
|
TArray<int> values;
|
|
if(!ParseNumArray(dataStr, values))
|
|
{
|
|
std::cout << "ProcessAttributes() - failed to parse int array attribute '" << dataAttrName
|
|
<< "' from string: '" << (dataStr.size()>128 ? dataStr.substr(0,64)+"<...>"+dataStr.substr(dataStr.size()-64) : dataStr)
|
|
<< "'." << std::endl;
|
|
continue;
|
|
}
|
|
IntVectorVars.Add(FString(name.c_str()), values);
|
|
}
|
|
else if(storage == "fpreal32")
|
|
{
|
|
TArray<float> values;
|
|
if(!ParseNumArray(dataStr, values))
|
|
{
|
|
std::cout << "ProcessAttributes() - failed to parse float array attribute '" << dataAttrName
|
|
<< "' from string: '" << (dataStr.size()>128 ? dataStr.substr(0,64)+"<...>"+dataStr.substr(dataStr.size()-64) : dataStr)
|
|
<< "'." << std::endl;
|
|
continue;
|
|
}
|
|
FloatVectorVars.Add(FString(name.c_str()), values);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Unsupported storage format '" << storage << "' for attribute: '" << dataAttrName
|
|
<< "'." << std::endl;
|
|
continue;
|
|
}
|
|
}
|
|
else if(type == "string")
|
|
{
|
|
it = attrs.Find(FString(std::string(name+".strings").c_str()));
|
|
const std::string &stringsStr = it != nullptr ? *it : "";
|
|
TArray<std::string> strings;
|
|
if(!ParseArray(stringsStr, strings))
|
|
{
|
|
std::cout << "ProcessAttributes() - failed to parse string array attribute: '" << name
|
|
<< ".strings'" << std::endl;
|
|
continue;
|
|
}
|
|
it = attrs.Find(FString(std::string(name+".indices.arrays").c_str()));
|
|
const std::string &indicesStr = it != nullptr ? *it : "";
|
|
TArray<int> indices;
|
|
if(!ParseNumArray(indicesStr, indices))
|
|
{
|
|
std::cout << "ProcessAttributes() - failed to parse int array attribute '" << name
|
|
<< ".indices.arrays' from string: '"
|
|
<< (indicesStr.size()>128 ? indicesStr.substr(0,64)+"<...>"+indicesStr.substr(indicesStr.size()-64) : indicesStr)
|
|
<< "'." << std::endl;
|
|
continue;
|
|
}
|
|
IndexedStringVars.Add(
|
|
FString(name.c_str()),
|
|
TPair<TArray<std::string>, TArray<int32>>(strings, indices));
|
|
}
|
|
else
|
|
{
|
|
std::cout << "ProcessAttributes() - unsupported attr type: '" << type << "'." << std::endl;
|
|
continue;
|
|
}
|
|
}// end for attrNames
|
|
}// and for catToAttrs
|
|
}
|
|
|
|
bool
|
|
ReadGEO_v19(
|
|
const std::string &filename,
|
|
TMap<FString, int32>& IntVars,
|
|
TMap<FString, TArray<int32>>& IntVectorVars,
|
|
TMap<FString, TArray<float>>& FloatVectorVars,
|
|
TMap<FString, TPair<TArray<std::string>, TArray<int32>>>& IndexedStringVars,
|
|
std::ostream *errorStream);
|
|
|
|
std::unique_ptr<std::istream>
|
|
unzip(const std::string& filename, std::unique_ptr<IFileHandle>& infile, std::ios::openmode mode=std::ios::in)
|
|
{
|
|
std::unique_ptr<std::istream> stream;
|
|
FPlatformFileManager& FileManager = FPlatformFileManager::Get();
|
|
IPlatformFile& PlatformFile = FileManager.GetPlatformFile();
|
|
infile.reset(PlatformFile.OpenRead(*FString(filename.c_str()), false));
|
|
|
|
FString errmsg;
|
|
#ifdef USE_ZLIB
|
|
if(GZIP_FILE_HEADER header; header.Read(infile.get(), errmsg))
|
|
{
|
|
infile->Seek(0);
|
|
stream.reset(new ZIP_FILE_ISTREAM(infile.get(), false));
|
|
}
|
|
else
|
|
#endif // USE_ZLIB
|
|
{
|
|
infile->Seek(0);
|
|
stream.reset(new IFileStream(infile.get()));
|
|
}
|
|
if(stream)
|
|
stream->imbue(std::locale::classic());
|
|
return stream;
|
|
}
|
|
|
|
bool
|
|
ReadGEO(
|
|
const std::string &filename,
|
|
TMap<FString, int32>& IntVars,
|
|
TMap<FString, TArray<int32>>& IntVectorVars,
|
|
TMap<FString, TArray<float>>& FloatVectorVars,
|
|
TMap<FString, TPair<TArray<std::string>, TArray<int32>>>& IndexedStringVars,
|
|
std::ostream *errorStream)
|
|
{
|
|
std::unique_ptr<IFileHandle> infile = nullptr;
|
|
std::unique_ptr<std::istream> input(unzip(filename, infile));
|
|
if(!*input)
|
|
{
|
|
if(errorStream) *errorStream<<"ReadGEO(): Can't open GEO file: '"<<filename<<"'"<<endl;
|
|
return false;
|
|
}
|
|
std::istream &inref = *input;
|
|
|
|
// Determine file version and forward to the proper read routine.
|
|
|
|
inref >> std::ws;
|
|
std::string buffer; buffer.reserve(1024);
|
|
inref >> buffer;
|
|
if(buffer == "[") // [ "fileversion","19.0.518",
|
|
{
|
|
inref >> std::ws;
|
|
inref >> buffer;
|
|
if(buffer.substr(0,13) == "\"fileversion\"")
|
|
{
|
|
input.reset(); // close file
|
|
if(buffer.substr(15, 3) == "19.") // match major version
|
|
return ReadGEO_v19(filename, IntVars, IntVectorVars, FloatVectorVars, IndexedStringVars, errorStream);
|
|
else
|
|
{
|
|
if(errorStream)
|
|
(*errorStream) << "Warning: using v19 GEO parser for file: '" << filename << "'." << std::endl;
|
|
return ReadGEO_v19(filename, IntVars, IntVectorVars, FloatVectorVars, IndexedStringVars, errorStream);
|
|
}
|
|
}
|
|
}
|
|
else if(buffer == "PGEOMETRY") // PGEOMETRY V5
|
|
{
|
|
#ifdef READ_GEO_LEGACY
|
|
inref >> std::ws;
|
|
inref >> buffer;
|
|
input.reset(); // close file
|
|
if(buffer == "V5")
|
|
return readGEO_Legacy(filename, headersOnly, errorStream);
|
|
else
|
|
{
|
|
if(errorStream)
|
|
(*errorStream) << "Warning: using legacy V5 GEO parser for file: '" << filename << "'." << std::endl;
|
|
return readGEO_Legacy(filename, headersOnly, errorStream);
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
infile.reset();
|
|
input.reset(); // close file
|
|
|
|
#ifdef READ_GEO_LEGACY
|
|
if(errorStream)
|
|
(*errorStream) << "Warning: Failed to determine GEO file version; using legacy V5 GEO parser for file: '" << filename << "'." << std::endl;
|
|
return readGEO_Legacy(filename, headersOnly, errorStream);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
ReadGEO_v19(
|
|
const std::string& filename,
|
|
TMap<FString, int32>& IntVars,
|
|
TMap<FString, TArray<int32>>& IntVectorVars,
|
|
TMap<FString, TArray<float>>& FloatVectorVars,
|
|
TMap<FString, TPair<TArray<std::string>, TArray<int32>>>& IndexedStringVars,
|
|
std::ostream* errorStream)
|
|
{
|
|
std::unique_ptr<IFileHandle> infile = nullptr;
|
|
std::unique_ptr<std::istream> input(unzip(filename, infile));
|
|
if(!*input)
|
|
{
|
|
if(errorStream) *errorStream<<"Partio: Can't open GEO file: '"<<filename<<"'"<<endl;
|
|
return false;
|
|
}
|
|
std::istream &inref = *input;
|
|
|
|
TMap<FString, std::string> kvp;
|
|
if(!ParseGeoKVPairs(inref, kvp, filename.c_str(), errorStream))
|
|
{
|
|
if(errorStream) *errorStream<<"Partio: Failed to parse GEO file: '" << filename << "'" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
TMap<FString, TMap<FString, std::string>> attributes;
|
|
const auto *attributesIt = kvp.Find("attributes");
|
|
if(attributesIt != nullptr)
|
|
{
|
|
if(!ParseAttributes(*attributesIt, attributes))
|
|
return false;
|
|
kvp.Remove("attributes");
|
|
}
|
|
|
|
TMap<FString, std::string> topology;
|
|
const auto topologyIt = kvp.Find("topology");
|
|
if(topologyIt != nullptr)
|
|
{
|
|
if(!ParseTopology(*topologyIt, topology))
|
|
return false;
|
|
kvp.Remove("topology");
|
|
}
|
|
|
|
TMap<FString, std::string> primitives;
|
|
const auto primitivesIt = kvp.Find("primitives");
|
|
if(primitivesIt != nullptr)
|
|
{
|
|
if(!ParsePrimitives(*primitivesIt, primitives))
|
|
return false;
|
|
kvp.Remove("primitives");
|
|
}
|
|
|
|
TMap<FString, std::string> info;
|
|
if(!FlattenDictionaries(kvp, info))
|
|
{
|
|
if(errorStream) *errorStream<<"ReadGEO_v19(): Failed to flatten GEO file: '" << filename << "'" << std::endl;
|
|
return false;
|
|
}
|
|
/*
|
|
std::cout << "readGEO() - flattened kvp dump:" << std::endl;
|
|
DebugPrint(info);
|
|
std::cout << std::endl;
|
|
std::cout << "readGEO() - primitives:" << std::endl;
|
|
DebugPrint(primitives);
|
|
std::cout << std::endl;
|
|
std::cout << "readGEO() - topology:" << std::endl;
|
|
DebugPrint(topology);
|
|
std::cout << std::endl;
|
|
std::cout << "readGEO() - attributes:" << std::endl;
|
|
DebugPrint(attributes, " attributes");
|
|
std::cout << std::endl;
|
|
*/
|
|
ProcessInfo(IntVars, info);
|
|
ProcessPrimitivesAndTopology(IntVars, IntVectorVars, primitives, topology);
|
|
ProcessAttributes(IntVars, IntVectorVars, FloatVectorVars, IndexedStringVars, attributes);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#ifdef READ_GEO_LEGACY
|
|
ParticlesDataMutable* readGEO_Legacy(const char* filename,const bool headersOnly,std::ostream* errorStream)
|
|
{
|
|
unique_ptr<istream> input(io::unzip(filename));
|
|
if(!*input){
|
|
if(errorStream) *errorStream<<"Partio: Can't open particle data file: "<<filename<<endl;
|
|
return 0;
|
|
}
|
|
int NPoints=0, NPointAttrib=0, NIndices;
|
|
|
|
ParticlesDataMutable* simple=0;
|
|
if(headersOnly) simple=new ParticleHeaders;
|
|
else simple=create();
|
|
|
|
// read NPoints and NPointAttrib
|
|
string word;
|
|
while(input->good()){
|
|
*input>>word;
|
|
if(word=="NPoints"||word=="pointcount") *input>>NPoints;
|
|
else if(word=="vertexcount") *input>>NIndices;
|
|
else if(word=="NPointAttrib")
|
|
{
|
|
*input>>NPointAttrib;
|
|
break;
|
|
}
|
|
}
|
|
// skip until PointAttrib
|
|
while(input->good()){
|
|
*input>>word;
|
|
if(word=="PointAttrib") break;
|
|
}
|
|
// read attribute descriptions
|
|
int attrInfoRead = 0;
|
|
|
|
ParticleAttribute positionAttr=simple->addAttribute("position",VECTOR,3);
|
|
ParticleAccessor positionAccessor(positionAttr);
|
|
|
|
vector<ParticleAttribute> attrs;
|
|
vector<ParticleAccessor> accessors;
|
|
while (input->good() && attrInfoRead < NPointAttrib) {
|
|
string attrName, attrType;
|
|
int nvals = 0;
|
|
*input >> attrName >> nvals >> attrType;
|
|
if(attrType=="index"){
|
|
if(errorStream) *errorStream<<"Partio: attr '"<<attrName<<"' of type index (string) found, treating as integer"<<endl;
|
|
int nIndices=0;
|
|
*input>>nIndices;
|
|
ParticleAttribute attribute=simple->addAttribute(attrName.c_str(),INDEXEDSTR,1);
|
|
attrs.push_back(attribute);
|
|
for(int j=0;j<nIndices;j++){
|
|
string indexName;
|
|
// *input>>indexName;
|
|
indexName=scanString(*input);
|
|
if (!headersOnly) {
|
|
int id=simple->registerIndexedStr(attribute,indexName.c_str());
|
|
if(id != j){
|
|
if(errorStream) *errorStream<<"Partio: error on read, expected registerIndexStr to return index "<<j<<" but got "<<id<<" for string "<<indexName<<endl;
|
|
}
|
|
}
|
|
}
|
|
accessors.push_back(ParticleAccessor(attrs.back()));
|
|
attrInfoRead++;
|
|
|
|
}else{
|
|
for (int i=0;i<nvals;i++) {
|
|
float defval;
|
|
*input>>defval;
|
|
}
|
|
ParticleAttributeType type;
|
|
// TODO: fix for other attribute types
|
|
if(attrType=="float") type=FLOAT;
|
|
else if(attrType=="vector") type=VECTOR;
|
|
else if(attrType=="int") type=INT;
|
|
else{
|
|
if(errorStream) *errorStream<<"Partio: unknown attribute "<<attrType<<" type... aborting"<<endl;
|
|
type=NONE;
|
|
}
|
|
attrs.push_back(simple->addAttribute(attrName.c_str(),type,nvals));
|
|
accessors.push_back(ParticleAccessor(attrs.back()));
|
|
attrInfoRead++;
|
|
}
|
|
}
|
|
|
|
simple->addParticles(NPoints);
|
|
|
|
ParticlesDataMutable::iterator iterator=simple->begin();
|
|
iterator.addAccessor(positionAccessor);
|
|
for(size_t i=0;i<accessors.size();i++) iterator.addAccessor(accessors[i]);
|
|
|
|
if(headersOnly) return simple; // escape before we try to touch data
|
|
|
|
|
|
float fval;
|
|
// TODO: fix
|
|
for(ParticlesDataMutable::iterator end=simple->end();iterator!=end && input->good();++iterator){
|
|
float* posInternal=positionAccessor.raw<float>(iterator);
|
|
for(int i=0;i<3;i++) *input>>posInternal[i];
|
|
*input>>fval;
|
|
//cout<<"saw "<<posInternal[0]<<" "<<posInternal[1]<<" "<<posInternal[2]<<endl;
|
|
|
|
// skip open paren
|
|
char paren = 0;
|
|
*input>> paren;
|
|
if (paren != '(') break;
|
|
|
|
// read additional attribute values
|
|
for (unsigned int i=0;i<attrs.size();i++){
|
|
switch(attrs[i].type){
|
|
case NONE: assert(false);break;
|
|
case FLOAT: readGeoAttr<FLOAT>(*input,attrs[i],accessors[i],iterator);break;
|
|
case VECTOR: readGeoAttr<VECTOR>(*input,attrs[i],accessors[i],iterator);break;
|
|
case INT: readGeoAttr<INT>(*input,attrs[i],accessors[i],iterator);break;
|
|
case INDEXEDSTR: readGeoAttr<INDEXEDSTR>(*input,attrs[i],accessors[i],iterator);break;
|
|
}
|
|
}
|
|
// skip closing parenthes
|
|
*input >> paren;
|
|
if (paren != ')') break;
|
|
}
|
|
return simple;
|
|
}
|
|
#endif //READ_GEO_LEGACY
|
|
|
|
} // namespace ChaosFlesh
|
|
|
|
#endif // WITH_EDITOR
|