GlsAnimation Library  1.0.6
Provides classes and functions to support animating GL Studio objects programmatically or with script files
gls_animation_parser.cpp
Go to the documentation of this file.
1 
41 #include "gls_animation.h"
42 #include "tinyxml2.h"
43 #ifdef GLS_ANIMATION_ENABLE_ZIP_LOADING
44 # include "unzip.h"
45 #endif
46 
47 #include <algorithm>
48 #include <fstream>
49 #include <sstream>
50 
51 namespace disti
52 {
53  namespace
54  {
55  // This list of strings represents the strings we expect to find in the file to be parsed.
56  // We use std::string where explicit comparisons are expected and char* where not.
57  const std::string s_expectedVersion = "1.0";
58 
59  const char* const s_animationCollectionTag = "AnimationSequence";
60  const std::string s_animationPropertyTag = "AnimationProperty";
61 
62  const char* const s_nameTag = "name";
63  const char* const s_typeTag = "type";
64 
65  const std::string s_floatTypeTag = "float";
66  const std::string s_vec3fTypeTag = "vec3f";
67  const std::string s_vec4fTypeTag = "vec4f";
68  const std::string s_colorTypeTag = "color";
69  const std::string s_stringTypeTag = "string";
70 
71  const char s_typeSeparator = ':';
72 
73  const std::string s_keyTag = "Key";
74  const char* const s_timeTag = "t";
75  const char* const s_valueTag = "v";
76 
78  // For non-string values, parse the value.
79  template<class Value>
80  Value Destringify( const std::string& str, typename Detail::enable_if< !Detail::is_same< Value, std::string >::value >::type* = 0 )
81  {
82  std::istringstream iss( str );
83  Value value;
84  iss >> value;
85  return value;
86  }
87 
88  // For std::string, return the whole string without parsing.
89  template<class Value>
90  Value Destringify( const std::string& str, typename Detail::enable_if< Detail::is_same< Value, std::string >::value >::type* = 0 )
91  {
92  return str;
93  }
94 
96  template<class Value>
97  typename GlsKeyframeCurve<Value>::PtrConst ParseKeyframes( tinyxml2::XMLElement* const element, const std::string& interpType, const char* const name )
98  {
99  typename GlsKeyframeInterpolator<Value>::PtrConst interpolator( CreateInterpolator<Value>( interpType ) );
100  if( !interpolator )
101  {
102  return typename GlsKeyframeCurve<Value>::PtrConst();
103  }
104 
105  typename GlsKeyframeCurve<Value>::Ptr curve( new GlsKeyframeCurve<Value>( interpolator ) );
106 
107  for( tinyxml2::XMLElement* elemChild = element->FirstChildElement(); elemChild != NULL; elemChild = elemChild->NextSiblingElement() )
108  {
109  if( s_keyTag == elemChild->Value() )
110  {
111  double key;
112  if( const char* time = elemChild->Attribute( s_timeTag ) )
113  {
114  key = std::atof( time );
115  }
116  else
117  {
118  std::cerr << "LoadAnimationScript(): No key found for frame in '" << name << "'. Ignoring.\n";
119  continue;
120  }
121 
122  std::string valueStr;
123  if( const char* value = elemChild->Attribute( s_valueTag ) )
124  {
125  valueStr = value;
126  }
127  else if( const char* text = elemChild->GetText() )
128  {
129  valueStr = text;
130  }
131  else
132  {
133  std::cerr << "LoadAnimationScript(): Key '" << key << "' did not have a value. Ignoring.\n";
134  continue;
135  }
136 
137  curve->AddKeyframe( key, Destringify<Value>( valueStr ) );
138  }
139  }
140  return curve;
141  }
142 
144  GlsAnimation::Ptr LoadAnimation( const std::vector<char>& file, const std::string& name )
145  {
146  tinyxml2::XMLDocument doc;
147  if( doc.Parse( const_cast<char*>( &file[0] ), file.size()) != tinyxml2::XML_NO_ERROR )
148  {
149  std::cerr << "LoadAnimation(): Unable to parse script file " << name << ". (Bad format?)\n";
150  return GlsAnimation::Ptr();
151  }
152 
153  tinyxml2::XMLNode* frames = doc.FirstChildElement( s_animationCollectionTag );
154 
155  if( frames == 0 )
156  {
157  std::cerr << "LoadAnimation(): " << s_animationCollectionTag << " tag not found in " << name << std::endl;
158  return GlsAnimation::Ptr();
159  }
160 
161  // Validate the file version
162  tinyxml2::XMLElement* topElem = frames->ToElement();
163  DistiAssert( topElem );
164  const char* version = topElem->Attribute( "version" );
165  if( !version || version != s_expectedVersion )
166  {
167  std::cerr << "LoadAnimation(): Unknown version number (found=" << (version ? version : "no version")
168  << ", expected=" << s_expectedVersion << ")\n";
169  return GlsAnimation::Ptr();
170  }
171 
172  // Parse the file
173  GlsAnimationCollection::Ptr animationSeq( new GlsAnimationCollection( name ) );
174  for( tinyxml2::XMLElement* child = frames->FirstChildElement(); child != NULL; child = child->NextSiblingElement())
175  {
176  const char* childValue = child->Value();
177  if( childValue && s_animationPropertyTag == childValue )
178  {
179  const char* animationName = child->Attribute( s_nameTag );
180  const char* rawType = child->Attribute( s_typeTag );
181  if( !rawType || !animationName )
182  {
183  std::cerr << "LoadAnimation(): Name and/or type tags not set(Name=" << (animationName ? animationName : "Not Set")
184  << ", type=" << (rawType ? rawType : "Not Set") << ")\n";
185  continue;
186  }
187  std::string baseType = rawType;
188  const std::size_t sepPos = baseType.find( s_typeSeparator );
189  std::string interpType = sepPos != std::string::npos ? baseType.substr( sepPos + 1 ) : std::string();
190  if( !interpType.empty() )
191  {
192  baseType = baseType.substr( 0, sepPos );
193  }
194 
195  if( s_floatTypeTag == baseType )
196  {
197  const GlsKeyframeCurve<double>::PtrConst curve = ( ParseKeyframes<double>( child, interpType, animationName ) );
198  if( curve && curve->GetKeyframeCount() > 0 )
199  {
200  animationSeq->Add( new GlsKeyframeAnimation( name, animationName, curve ) );
201  }
202  }
203  else if( s_vec3fTypeTag == baseType )
204  {
205  const GlsKeyframeCurve<Vector>::PtrConst curve = ParseKeyframes<Vector>( child, interpType, animationName );
206  if( curve && curve->GetKeyframeCount() > 0 )
207  {
208  animationSeq->Add( new GlsKeyframeAnimation( name, animationName, curve ) );
209  }
210  }
211  else if( s_vec4fTypeTag == baseType )
212  {
213  const GlsKeyframeCurve<GlsQuaternionD>::PtrConst curve = ParseKeyframes<GlsQuaternionD>( child, interpType, animationName );
214  if( curve && curve->GetKeyframeCount() > 0 )
215  {
216  animationSeq->Add( new GlsKeyframeAnimation( name, animationName, curve ) );
217  }
218  }
219  else if( s_colorTypeTag == baseType )
220  {
221  const GlsKeyframeCurve<glsColor>::PtrConst curve = ParseKeyframes<glsColor>( child, interpType, animationName );
222  if( curve && curve->GetKeyframeCount() > 0 )
223  {
224  animationSeq->Add( new GlsKeyframeAnimation( name, animationName, curve ) );
225  }
226  }
227  else if( s_stringTypeTag == baseType )
228  {
229  if( !interpType.empty() && interpType != g_gatedInterpTag )
230  {
231  std::cerr << "LoadAnimation(): Forcing string to gated interpolation: " << name << " of type " << rawType << std::endl;
232  }
233  const GlsKeyframeCurve<std::string>::PtrConst curve = ParseKeyframes<std::string>( child, g_gatedInterpTag, animationName );
234  if( curve && curve->GetKeyframeCount() > 0 )
235  {
236  animationSeq->Add( new GlsKeyframeAnimation( name, animationName, curve ) );
237  }
238  }
239  else
240  {
241  std::cerr << "LoadAnimation(): Unknown type tag found (type=" << baseType.c_str()
242  << ", name=" << animationName << "). Ignoring.\n";
243  }
244  }
245  else
246  {
247  std::cerr << "LoadAnimation(): Unknown property tag found (property=" << (childValue ? childValue : "N/A")
248  << ", expected=" << s_animationPropertyTag << "). Ignoring.\n";
249  }
250  }
251  if( animationSeq->GetAnimationCount() == 0 )
252  {
253  std::cerr << "LoadAnimation(): No keys found in file " << name << std::endl;
254  return GlsAnimation::Ptr();
255  }
256  return animationSeq;
257  }
258  }
259 
260 #ifdef GLS_ANIMATION_ENABLE_ZIP_LOADING
261  std::vector< GlsAnimation::Ptr > LoadAnimationZip( const std::string& filename )
263  {
264  std::vector< GlsAnimation::Ptr > animations;
265 
266 
267  unzFile zipFile = unzOpen(filename.c_str());
268 
269  if (unzGoToFirstFile(zipFile) != UNZ_OK)
270  {
271  std::cerr << "LoadAnimationZip(): Unable to open file " << filename << std::endl;
272  return animations;
273  }
274 
275  const unsigned int bufSize(4096);
276  std::vector<char> buf(bufSize);
277  unz_file_info file_info;
278  do
279  {
280 
281  if( unzGetCurrentFileInfo(zipFile, &file_info, &buf[0], bufSize - 1, NULL,0, NULL,0) != UNZ_OK )
282  {
283  std::cerr << "LoadAnimationZip(): Error getting file info from file in " << filename << std::endl;
284  continue;
285  }
286 
287  std::string zippedFilename = &buf[0];
288 
289  std::string extension = zippedFilename.substr( zippedFilename.rfind('.') + 1 );
290  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
291  if (extension == "zip")
292  {
293  std::cerr << "LoadAnimationZip(): File '" << filename << "' contains a zip file. Nested zip files are not supported. Ignoring.\n";
294  continue;
295  }
296 
297  const uLong size = file_info.uncompressed_size;
298 
299  if( unzOpenCurrentFile(zipFile) != UNZ_OK )
300  {
301  std::cerr << "LoadAnimationZip(): Error opening file " << zippedFilename << " in file " << filename << std::endl;
302  continue;
303  }
304 
305  std::vector<char> data(size);
306 
307  if( unzReadCurrentFile(zipFile,&data[0],size) != static_cast<int>( size ) )
308  {
309  std::cerr << "LoadAnimationZip(): Error reading from file " << zippedFilename << " in file " << filename << std::endl;
310  continue;
311  }
312 
313  if( GlsAnimation::Ptr anim = LoadAnimation( data, zippedFilename ) )
314  {
315  animations.push_back( anim );
316  }
317 
318 
319  } while(unzGoToNextFile(zipFile) == UNZ_OK);
320 
321  return animations;
322  }
323 #endif
324 
326  GlsAnimation::Ptr LoadAnimationScript( const std::string& filename )
327  {
328  // Load the document
329  std::ifstream readIn( filename.c_str(), std::ios_base::ate );
330 
331  if( !readIn )
332  {
333  std::cerr << "LoadAnimationScript(): Unable to load script file " << filename << std::endl;
334  return GlsAnimation::Ptr();
335  }
336 
337  std::streampos size = readIn.tellg();
338  readIn.seekg( 0 );
339 
340  if( !readIn || size == static_cast<std::streampos>(-1) ) // tellg returns -1 on an error
341  {
342  std::cerr << "LoadAnimationScript(): Error reading from file " << filename << std::endl;
343  return GlsAnimation::Ptr();
344  }
345 
346  std::vector<char> data( static_cast<unsigned int>( size ) );
347  readIn.read( &data[0], size );
348 
349  return LoadAnimation( data, filename );
350  }
351 }
stdortr1::shared_ptr< const GlsKeyframeCurve > PtrConst
Alias for easier reading.
stdortr1::shared_ptr< GlsAnimationCollection > Ptr
Alias for easier reading.
GlsAnimation::Ptr LoadAnimationScript(const std::string &filename)
Loads an animation sequence from a script file.
stdortr1::shared_ptr< GlsAnimation > Ptr
Alias for easier reading.
Definition: gls_animation.h:60
const std::string g_gatedInterpTag
Gated interpolator tag.
stdortr1::shared_ptr< const GlsKeyframeInterpolator > PtrConst
Alias for easier reading.
Definition: gls_keyframe.h:167
The GL Studio animation classes.
std::vector< GlsAnimation::Ptr > LoadAnimationZip(const std::string &filename)
Loads a set of animation sequences from a zip file.