--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
# include "EmberPch.h"
# include "XmlToEmber.h"
namespace EmberNs
{
/// <summary>
/// Constructor which saves the state of the current locale and
/// sets the new one based on the parameters passed in.
/// </summary>
/// <param name="category">The locale category. Default: LC_NUMERIC.</param>
/// <param name="loc">The locale. Default: "C".</param>
Locale : : Locale ( int category , const char * loc )
{
m_Category = category ;
m_NewLocale = string ( loc ) ;
m_OriginalLocale = setlocale ( category , nullptr ) ; //Query.
if ( m_OriginalLocale . empty ( ) )
cout < < " Couldn't get original locale. \n " ;
if ( setlocale ( category , loc ) = = nullptr ) //Set.
cout < < " Couldn't set new locale " < < category < < " , " < < loc < < " . \n " ;
}
/// <summary>
/// Reset the locale to the value stored during construction.
/// </summary>
Locale : : ~ Locale ( )
{
if ( ! m_OriginalLocale . empty ( ) )
if ( setlocale ( m_Category , m_OriginalLocale . c_str ( ) ) = = nullptr ) //Restore.
cout < < " Couldn't restore original locale " < < m_Category < < " , " < < m_OriginalLocale < < " . \n " ;
}
/// <summary>
/// Constructor that initializes the random context.
/// </summary>
template < typename T >
XmlToEmber < T > : : XmlToEmber ( )
2017-02-26 12:34:43 -05:00
: m_VariationList ( VariationList < T > : : Instance ( ) ) ,
m_PaletteList ( PaletteList < float > : : Instance ( ) )
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
{
Timing t ;
if ( ! m_Init )
{
//This list is for variation params which are incorrect, but their parent variation name may or may not be correct.
//This has some overlap with the list below since some of these have parent variation names that are incorrect.
m_BadParamNames = unordered_map < string , string >
{
{ " swtin_distort " , " stwin_distort " } , //stwin.
{ " pow_numerator " , " pow_block_numerator " } , //pow_block.
{ " pow_denominator " , " pow_block_denominator " } ,
{ " pow_root " , " pow_block_root " } ,
{ " pow_correctn " , " pow_block_correctn " } ,
{ " pow_correctd " , " pow_block_correctd " } ,
{ " pow_power " , " pow_block_power " } ,
{ " lt " , " linearT_powX " } , //linearT.
{ " lt " , " linearT_powY " } ,
{ " re_a " , " Mobius_Re_A " } , //Mobius.
{ " im_a " , " Mobius_Im_A " } ,
{ " re_b " , " Mobius_Re_B " } ,
{ " im_b " , " Mobius_Im_B " } ,
{ " re_c " , " Mobius_Re_C " } ,
{ " im_c " , " Mobius_Im_C " } ,
{ " re_d " , " Mobius_Re_D " } ,
{ " im_d " , " Mobius_Im_D " } ,
{ " rx_sin " , " rotate_x_sin " } , //rotate_x.
{ " rx_cos " , " rotate_x_cos " } ,
{ " ry_sin " , " rotate_y_sin " } , //rotate_y.
{ " ry_cos " , " rotate_y_cos " } ,
{ " intrfr2_a1 " , " interference2_a1 " } , //interference2.
{ " intrfr2_b1 " , " interference2_b1 " } ,
{ " intrfr2_c1 " , " interference2_c1 " } ,
{ " intrfr2_p1 " , " interference2_p1 " } ,
{ " intrfr2_t1 " , " interference2_t1 " } ,
{ " intrfr2_a2 " , " interference2_a2 " } ,
{ " intrfr2_b2 " , " interference2_b2 " } ,
{ " intrfr2_c2 " , " interference2_c2 " } ,
{ " intrfr2_p2 " , " interference2_p2 " } ,
{ " intrfr2_t2 " , " interference2_t2 " } ,
{ " octa_x " , " octagon_x " } , //octagon.
{ " octa_y " , " octagon_y " } ,
{ " octa_z " , " octagon_z " } ,
{ " bubble_x " , " bubble2_x " } , //bubble2.
{ " bubble_y " , " bubble2_y " } ,
{ " bubble_z " , " bubble2_z " } ,
{ " cubic3d_xpand " , " cubicLattice_3D_xpand " } , //cubicLattice_3D.
{ " cubic3d_style " , " cubicLattice_3D_style " } ,
{ " splitb_x " , " SplitBrdr_x " } , //SplitBrdr.
{ " splitb_y " , " SplitBrdr_y " } ,
{ " splitb_px " , " SplitBrdr_px " } ,
{ " splitb_py " , " SplitBrdr_py " } ,
{ " dc_cyl_offset " , " dc_cylinder_offset " } , //dc_cylinder.
{ " dc_cyl_angle " , " dc_cylinder_angle " } ,
{ " dc_cyl_scale " , " dc_cylinder_scale " } ,
{ " cyl_x " , " dc_cylinder_x " } ,
{ " cyl_y " , " dc_cylinder_y " } ,
{ " cyl_blur " , " dc_cylinder_blur " } ,
{ " mobius_radius " , " mobius_strip_radius " } , //mobius_strip.
{ " mobius_width " , " mobius_strip_width " } ,
{ " mobius_rect_x " , " mobius_strip_rect_x " } ,
{ " mobius_rect_y " , " mobius_strip_rect_y " } ,
{ " mobius_rotate_x " , " mobius_strip_rotate_x " } ,
{ " mobius_rotate_y " , " mobius_strip_rotate_y " } ,
{ " bwraps2_cellsize " , " bwraps_cellsize " } , //bwraps2.
{ " bwraps2_space " , " bwraps_space " } ,
{ " bwraps2_gain " , " bwraps_gain " } ,
{ " bwraps2_inner_twist " , " bwraps_inner_twist " } ,
{ " bwraps2_outer_twist " , " bwraps_outer_twist " } ,
{ " bwraps7_cellsize " , " bwraps_cellsize " } , //bwraps7.
{ " bwraps7_space " , " bwraps_space " } ,
{ " bwraps7_gain " , " bwraps_gain " } ,
{ " bwraps7_inner_twist " , " bwraps_inner_twist " } ,
{ " bwraps7_outer_twist " , " bwraps_outer_twist " } ,
{ " pre_bwraps2_cellsize " , " pre_bwraps_cellsize " } , //bwraps2.
{ " pre_bwraps2_space " , " pre_bwraps_space " } ,
{ " pre_bwraps2_gain " , " pre_bwraps_gain " } ,
{ " pre_bwraps2_inner_twist " , " pre_bwraps_inner_twist " } ,
{ " pre_bwraps2_outer_twist " , " pre_bwraps_outer_twist " } ,
{ " post_bwraps2_cellsize " , " post_bwraps_cellsize " } ,
{ " post_bwraps2_space " , " post_bwraps_space " } ,
{ " post_bwraps2_gain " , " post_bwraps_gain " } ,
{ " post_bwraps2_inner_twist " , " post_bwraps_inner_twist " } ,
{ " post_bwraps2_outer_twist " , " post_bwraps_outer_twist " } ,
{ " hexa3d_majp " , " hexaplay3D_majp " } , //hexaplay3D.
{ " hexa3d_scale " , " hexaplay3D_scale " } ,
{ " hexa3d_zlift " , " hexaplay3D_zlift " } ,
{ " nb_numedges " , " nBlur_numEdges " } , //nBlur.
{ " nb_numstripes " , " nBlur_numStripes " } ,
{ " nb_ratiostripes " , " nBlur_ratioStripes " } ,
{ " nb_ratiohole " , " nBlur_ratioHole " } ,
{ " nb_circumcircle " , " nBlur_circumCircle " } ,
{ " nb_adjusttolinear " , " nBlur_adjustToLinear " } ,
{ " nb_equalblur " , " nBlur_equalBlur " } ,
{ " nb_exactcalc " , " nBlur_exactCalc " } ,
{ " nb_highlightedges " , " nBlur_highlightEdges " } ,
{ " octapol_r " , " octapol_radius " } , //octapol.
{ " number_of_stripes " , " bubbleT3D_number_of_stripes " } , //bubbleT3D.
{ " ratio_of_stripes " , " bubbleT3D_ratio_of_stripes " } ,
{ " angle_of_hole " , " bubbleT3D_angle_of_hole " } ,
{ " exponentZ " , " bubbleT3D_exponentZ " } ,
{ " _symmetryZ " , " bubbleT3D_symmetryZ " } ,
{ " _modusBlur " , " bubbleT3D_modusBlur " } ,
{ " post_scrop_power " , " post_smartcrop_power " } , //post_smartcrop.
{ " post_scrop_radius " , " post_smartcrop_radius " } ,
{ " post_scrop_roundstr " , " post_smartcrop_roundstr " } ,
{ " post_scrop_roundwidth " , " post_smartcrop_roundwidth " } ,
{ " post_scrop_distortion " , " post_smartcrop_distortion " } ,
{ " post_scrop_edge " , " post_smartcrop_edge " } ,
{ " post_scrop_scatter " , " post_smartcrop_scatter " } ,
{ " post_scrop_offset " , " post_smartcrop_offset " } ,
{ " post_scrop_rotation " , " post_smartcrop_rotation " } ,
{ " post_scrop_cropmode " , " post_smartcrop_cropmode " } ,
{ " post_scrop_static " , " post_smartcrop_static " } ,
{ " cs_radius " , " circlesplit_radius " } ,
{ " cs_split " , " circlesplit_split " } ,
{ " w2r_freqx " , " waves2_radial_freqx " } ,
{ " w2r_scalex " , " waves2_radial_scalex " } ,
{ " w2r_freqy " , " waves2_radial_freqy " } ,
{ " w2r_scaley " , " waves2_radial_scaley " } ,
{ " w2r_null " , " waves2_radial_null " } ,
{ " w2r_distance " , " waves2_radial_distance " } ,
{ " tf_exponent " , " Truchet_fill_exponent " } ,
{ " tf_arc_width " , " Truchet_fill_arc_width " } ,
{ " tf_seed " , " Truchet_fill_seed " }
} ;
m_FlattenNames =
{
" pre_crop " ,
" pre_falloff2 " ,
" pre_rotate_x " ,
" pre_rotate_y " ,
" pre_ztranslate " ,
" blur3D " ,
" bubble " ,
" bwraps " ,
" bwraps2 " ,
" crop " ,
" cylinder " ,
" falloff2 " ,
" hemisphere " ,
" julia3D " ,
" julia3Dz " ,
" linear3D " ,
" zblur " ,
" zcone " ,
" ztranslate " ,
" post_crop " ,
" post_falloff2 " ,
" post_rotate_x " ,
" post_rotate_y " ,
" curl3D_cz " ,
} ;
//This is a vector of the incorrect variation names and their param names as they are in the legacy, badly named flam3/Apophysis code.
vector < string > badParams =
{
" bwraps7_cellsize " ,
" bwraps7_space " ,
" bwraps7_gain " ,
" bwraps7_inner_twist " ,
" bwraps7_outer_twist "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " bwraps7 " ) , string ( " bwraps " ) ) , badParams ) ) ; //bwraps7 is the same as bwraps.
badParams =
{
" bwraps2_cellsize " ,
" bwraps2_space " ,
" bwraps2_gain " ,
" bwraps2_inner_twist " ,
" bwraps2_outer_twist "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " bwraps2 " ) , string ( " bwraps " ) ) , badParams ) ) ; //bwraps2 is the same as bwraps.
badParams =
{
" pre_bwraps2_cellsize " ,
" pre_bwraps2_space " ,
" pre_bwraps2_gain " ,
" pre_bwraps2_inner_twist " ,
" pre_bwraps2_outer_twist "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " pre_bwraps2 " ) , string ( " pre_bwraps " ) ) , badParams ) ) ;
badParams =
{
" post_bwraps2_cellsize " ,
" post_bwraps2_space " ,
" post_bwraps2_gain " ,
" post_bwraps2_inner_twist " ,
" post_bwraps2_outer_twist "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " post_bwraps2 " ) , string ( " post_bwraps " ) ) , badParams ) ) ;
badParams =
{
" mobius_radius " ,
" mobius_width " ,
" mobius_rect_x " ,
" mobius_rect_y " ,
" mobius_rotate_x " ,
" mobius_rotate_y "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " mobius " ) , string ( " mobius_strip " ) ) , badParams ) ) ; //mobius_strip clashes with Mobius.
badParams =
{
" post_dcztransl_x0 " ,
" post_dcztransl_x1 " ,
" post_dcztransl_factor " ,
" post_dcztransl_overwrite " ,
" post_dcztransl_clamp "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " post_dcztransl " ) , string ( " post_dc_ztransl " ) ) , badParams ) ) ;
badParams =
{
" post_scrop_power " ,
" post_scrop_radius " ,
" post_scrop_roundstr " ,
" post_scrop_roundwidth " ,
" post_scrop_distortion " ,
" post_scrop_edge " ,
" post_scrop_scatter " ,
" post_scrop_offset " ,
" post_scrop_rotation " ,
" post_scrop_cropmode " ,
" post_scrop_static "
} ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " post_scrop " ) , string ( " post_smartcrop " ) ) , badParams ) ) ;
//Note that splits3D can't be done here because its param names are also used by splits.
badParams . clear ( ) ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " pre_blur " ) , string ( " pre_gaussian_blur " ) ) , badParams ) ) ; //No other special params for these.
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " pre_spin_z " ) , string ( " pre_rotate_z " ) ) , badParams ) ) ;
m_BadVariationNames . push_back ( make_pair ( make_pair ( string ( " post_spin_z " ) , string ( " post_rotate_z " ) ) , badParams ) ) ;
m_Init = true ;
}
}
/// <summary>
/// Parse the specified buffer and place the results in the container of embers passed in.
/// </summary>
/// <param name="buf">The buffer to parse</param>
/// <param name="filename">Full path and filename, optionally empty</param>
/// <param name="embers">The newly constructed embers based on what was parsed</param>
/// <param name="useDefaults">True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. Default: true.</param>
/// <returns>True if there were no errors, else false.</returns>
template < typename T >
template < typename Alloc , template < typename , typename > class C >
bool XmlToEmber < T > : : Parse ( byte * buf , const char * filename , C < Ember < T > , Alloc > & embers , bool useDefaults )
{
char * bn ;
const char * xmlPtr ;
const char * loc = __FUNCTION__ ;
size_t emberSize ;
size_t bufSize ;
xmlDocPtr doc ; //Parsed XML document tree.
xmlNodePtr rootnode ;
Locale locale ; //Sets and restores on exit.
//Timing t;
ClearErrorReport ( ) ;
//Parse XML string into internal document.
xmlPtr = CX ( & buf [ 0 ] ) ;
bufSize = strlen ( xmlPtr ) ;
//embers.reserve(bufSize / 2500);//The Xml text for an ember is around 2500 bytes, but can be much more. Pre-allocate to aovid unnecessary resizing.
doc = xmlReadMemory ( xmlPtr , int ( bufSize ) , filename , " ISO-8859-1 " , XML_PARSE_HUGE ) ;
//t.Toc("xmlReadMemory");
if ( doc = = nullptr )
{
doc = xmlReadMemory ( xmlPtr , int ( bufSize ) , filename , " UTF-8 " , XML_PARSE_HUGE ) ;
if ( doc = = nullptr )
{
doc = xmlReadMemory ( xmlPtr , int ( bufSize ) , filename , " UTF-16 " , XML_PARSE_HUGE ) ;
}
}
if ( doc = = nullptr )
{
AddToReport ( string ( loc ) + " : Error parsing xml file " + string ( filename ) ) ;
return false ;
}
//What is the root node of the document?
rootnode = xmlDocGetRootElement ( doc ) ;
//Scan for <flame> nodes, starting with this node.
//t.Tic();
bn = basename ( const_cast < char * > ( filename ) ) ;
ScanForEmberNodes ( rootnode , bn , embers , useDefaults ) ;
xmlFreeDoc ( doc ) ;
emberSize = embers . size ( ) ;
auto first = embers . begin ( ) ;
//t.Toc("ScanForEmberNodes");
//Check to see if the first control point or the second-to-last
//control point has interpolation="smooth". This is invalid
//and should be reset to linear (with a warning).
if ( emberSize > 0 )
{
if ( first - > m_Interp = = eInterp : : EMBER_INTERP_SMOOTH )
first - > m_Interp = eInterp : : EMBER_INTERP_LINEAR ;
if ( emberSize > = 2 )
{
auto secondToLast = Advance ( embers . begin ( ) , emberSize - 2 ) ;
if ( secondToLast - > m_Interp = = eInterp : : EMBER_INTERP_SMOOTH )
secondToLast - > m_Interp = eInterp : : EMBER_INTERP_LINEAR ;
}
}
//Finally, ensure that consecutive 'rotate' parameters never exceed
//a difference of more than 180 degrees (+/-) for interpolation.
//An adjustment of +/- 360 degrees is made until this is true.
if ( emberSize > 1 )
{
auto prev = embers . begin ( ) ;
auto second = Advance ( embers . begin ( ) , 1 ) ;
for ( auto it = second ; it ! = embers . end ( ) ; + + it )
{
//Only do this adjustment if not in compat mode.
if ( prev - > m_AffineInterp ! = eAffineInterp : : AFFINE_INTERP_COMPAT & & prev - > m_AffineInterp ! = eAffineInterp : : AFFINE_INTERP_OLDER )
{
while ( it - > m_Rotate < prev - > m_Rotate - 180 )
it - > m_Rotate + = 360 ;
while ( it - > m_Rotate > prev - > m_Rotate + 180 )
it - > m_Rotate - = 360 ;
}
prev = it ;
}
}
return true ;
}
/// <summary>
/// Parse the specified file and place the results in the container of embers passed in.
/// This will strip out ampersands because the Xml parser can't handle them.
/// </summary>
/// <param name="filename">Full path and filename</param>
/// <param name="embers">The newly constructed embers based on what was parsed</param>
/// <param name="useDefaults">True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. Default: true.</param>
/// <returns>True if there were no errors, else false.</returns>
template < typename T >
template < typename Alloc , template < typename , typename > class C >
bool XmlToEmber < T > : : Parse ( const char * filename , C < Ember < T > , Alloc > & embers , bool useDefaults )
{
const char * loc = __FUNCTION__ ;
string buf ;
//Ensure palette list is setup first.
2017-02-26 12:34:43 -05:00
if ( ! m_PaletteList - > Size ( ) )
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
{
AddToReport ( string ( loc ) + " : Palette list must be initialized before parsing embers. " ) ;
return false ;
}
if ( ReadFile ( filename , buf ) )
{
std : : replace ( buf . begin ( ) , buf . end ( ) , ' & ' , ' + ' ) ;
return Parse ( reinterpret_cast < byte * > ( const_cast < char * > ( buf . data ( ) ) ) , filename , embers , useDefaults ) ;
}
else
return false ;
}
/// <summary>
/// Thin wrapper around converting the string to a numeric value and return a bool indicating success.
/// See error report for errors.
/// </summary>
/// <param name="str">The string to convert</param>
/// <param name="val">The converted value</param>
/// <returns>True if success, else false.</returns>
template < typename T >
template < typename valT >
bool XmlToEmber < T > : : Aton ( const char * str , valT & val )
{
bool b = true ;
const char * loc = __FUNCTION__ ;
std : : istringstream istr ( str ) ;
istr > > val ;
if ( istr . bad ( ) | | istr . fail ( ) )
{
AddToReport ( string ( loc ) + " : Error converting " + string ( str ) ) ;
b = false ;
}
return b ;
}
/// <summary>
/// Scan the file for ember nodes, and parse them out into the container of embers.
/// </summary>
/// <param name="curNode">The current node to parse</param>
/// <param name="parentFile">The full path and filename</param>
/// <param name="embers">The newly constructed embers based on what was parsed</param>
/// <param name="useDefaults">True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present.</param>
template < typename T >
template < typename Alloc , template < typename , typename > class C >
void XmlToEmber < T > : : ScanForEmberNodes ( xmlNode * curNode , char * parentFile , C < Ember < T > , Alloc > & embers , bool useDefaults )
{
bool parseEmberSuccess ;
xmlNodePtr thisNode = nullptr ;
const char * loc = __FUNCTION__ ;
string parentFileString = string ( parentFile ) ;
//Original memset to 0, but the constructors should handle that.
//Loop over this level of elements.
for ( thisNode = curNode ; thisNode ; thisNode = thisNode - > next )
{
//Check to see if this element is a <ember> element.
if ( thisNode - > type = = XML_ELEMENT_NODE & & ! Compare ( thisNode - > name , " flame " ) )
{
Ember < T > currentEmber ; //Place this inside here so its constructor is called each time.
//Useful for parsing templates when not every member should be set.
if ( ! useDefaults )
currentEmber . Clear ( false ) ;
parseEmberSuccess = ParseEmberElement ( thisNode , currentEmber ) ;
if ( ! parseEmberSuccess )
{
//Original leaked memory here, ours doesn't.
AddToReport ( string ( loc ) + " : Error parsing ember element " ) ;
return ;
}
if ( currentEmber . PaletteIndex ( ) ! = - 1 )
{
2017-02-26 12:34:43 -05:00
if ( auto pal = m_PaletteList - > GetPaletteByFilename ( m_PaletteList - > m_DefaultFilename , currentEmber . PaletteIndex ( ) ) )
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
currentEmber . m_Palette = * pal ;
else
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.
{
Xform < T > xform ;
currentEmber . AddXform ( xform ) ;
}
currentEmber . CacheXforms ( ) ;
currentEmber . m_Index = embers . size ( ) ;
currentEmber . m_ParentFilename = parentFileString ;
embers . push_back ( currentEmber ) ;
}
else
{
//Check all of the children of this element.
ScanForEmberNodes ( thisNode - > children , parentFile , embers , useDefaults ) ;
}
}
}
/// <summary>
/// Parse an ember element.
/// </summary>
/// <param name="emberNode">The current node to parse</param>
/// <param name="currentEmber">The newly constructed ember based on what was parsed</param>
/// <returns>True if there were no errors, else false.</returns>
template < typename T >
bool XmlToEmber < T > : : ParseEmberElement ( xmlNode * emberNode , Ember < T > & currentEmber )
{
bool ret = true ;
bool fromEmber = false ;
size_t newLinear = 0 ;
char * attStr ;
const char * loc = __FUNCTION__ ;
int soloXform = - 1 ;
size_t i , count = 0 , index = 0 ;
xmlAttrPtr att , curAtt ;
xmlNodePtr editNode , childNode , motionNode ;
currentEmber . m_Palette . Clear ( ) ; //Wipe out the current palette.
att = emberNode - > properties ; //The top level element is a ember element, read the attributes of it and store them.
if ( att = = nullptr )
{
AddToReport ( string ( loc ) + " : <flame> element has no attributes " ) ;
return false ;
}
for ( curAtt = att ; curAtt ; curAtt = curAtt - > next )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( emberNode , curAtt - > name ) ) ;
//First parse out simple float reads.
if ( ParseAndAssign ( curAtt - > name , attStr , " time " , currentEmber . m_Time , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " scale " , currentEmber . m_PixelsPerUnit , ret ) ) { currentEmber . m_OrigPixPerUnit = currentEmber . m_PixelsPerUnit ; }
else if ( ParseAndAssign ( curAtt - > name , attStr , " rotate " , currentEmber . m_Rotate , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " zoom " , currentEmber . m_Zoom , ret ) ) { ClampGteRef < T > ( currentEmber . m_Zoom , 0 ) ; }
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_zoom " , currentEmber . m_Zoom , ret ) ) { ClampGteRef < T > ( currentEmber . m_Zoom , 0 ) ; } //JWildfire uses cam_zoom.
else if ( ParseAndAssign ( curAtt - > name , attStr , " filter " , currentEmber . m_SpatialFilterRadius , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " temporal_filter_width " , currentEmber . m_TemporalFilterWidth , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " temporal_filter_exp " , currentEmber . m_TemporalFilterExp , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " quality " , currentEmber . m_Quality , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " brightness " , currentEmber . m_Brightness , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " gamma " , currentEmber . m_Gamma , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " highlight_power " , currentEmber . m_HighlightPower , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " vibrancy " , currentEmber . m_Vibrancy , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " estimator_radius " , currentEmber . m_MaxRadDE , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " estimator_minimum " , currentEmber . m_MinRadDE , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " estimator_curve " , currentEmber . m_CurveDE , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " gamma_threshold " , currentEmber . m_GammaThresh , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_zpos " , currentEmber . m_CamZPos , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_persp " , currentEmber . m_CamPerspective , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_perspective " , currentEmber . m_CamPerspective , ret ) ) { } //Apo bug.
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_yaw " , currentEmber . m_CamYaw , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_pitch " , currentEmber . m_CamPitch , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " cam_dof " , currentEmber . m_CamDepthBlur , ret ) ) { }
//Parse simple int reads.
else if ( ParseAndAssign ( curAtt - > name , attStr , " palette " , currentEmber . m_Palette . m_Index , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " oversample " , currentEmber . m_Supersample , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " supersample " , currentEmber . m_Supersample , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " temporal_samples " , currentEmber . m_TemporalSamples , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " sub_batch_size " , currentEmber . m_SubBatchSize , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " fuse " , currentEmber . m_FuseCount , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " soloxform " , soloXform , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " new_linear " , newLinear , ret ) ) { }
//Parse more complicated reads that have multiple possible values.
else if ( ! Compare ( curAtt - > name , " interpolation " ) )
{
if ( ! _stricmp ( " linear " , attStr ) )
currentEmber . m_Interp = eInterp : : EMBER_INTERP_LINEAR ;
else if ( ! _stricmp ( " smooth " , attStr ) )
currentEmber . m_Interp = eInterp : : EMBER_INTERP_SMOOTH ;
else
AddToReport ( string ( loc ) + " : Unrecognized interpolation type " + string ( attStr ) ) ;
}
else if ( ! Compare ( curAtt - > name , " palette_interpolation " ) )
{
if ( ! _stricmp ( " hsv " , attStr ) )
currentEmber . m_PaletteInterp = ePaletteInterp : : INTERP_HSV ;
else if ( ! _stricmp ( " sweep " , attStr ) )
currentEmber . m_PaletteInterp = ePaletteInterp : : INTERP_SWEEP ;
else
AddToReport ( string ( loc ) + " : Unrecognized palette interpolation type " + string ( attStr ) ) ;
}
else if ( ! Compare ( curAtt - > name , " interpolation_space " ) | | ! Compare ( curAtt - > name , " interpolation_type " ) )
{
if ( ! _stricmp ( " linear " , attStr ) )
currentEmber . m_AffineInterp = eAffineInterp : : AFFINE_INTERP_LINEAR ;
else if ( ! _stricmp ( " log " , attStr ) )
currentEmber . m_AffineInterp = eAffineInterp : : AFFINE_INTERP_LOG ;
else if ( ! _stricmp ( " old " , attStr ) )
currentEmber . m_AffineInterp = eAffineInterp : : AFFINE_INTERP_COMPAT ;
else if ( ! _stricmp ( " older " , attStr ) )
currentEmber . m_AffineInterp = eAffineInterp : : AFFINE_INTERP_OLDER ;
else
AddToReport ( string ( loc ) + " : Unrecognized interpolation type " + string ( attStr ) ) ;
}
else if ( ! Compare ( curAtt - > name , " name " ) )
{
currentEmber . m_Name = string ( attStr ) ;
std : : replace ( currentEmber . m_Name . begin ( ) , currentEmber . m_Name . end ( ) , ' ' , ' _ ' ) ;
}
else if ( ! Compare ( curAtt - > name , " version " ) )
{
if ( ToLower ( string ( attStr ) ) . find ( " ember " ) ! = string : : npos )
fromEmber = true ;
}
else if ( ! Compare ( curAtt - > name , " size " ) )
{
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 " ) )
{
istringstream is ( attStr ) ;
is > > currentEmber . m_CenterX > > currentEmber . m_CenterY ;
currentEmber . m_RotCenterY = currentEmber . m_CenterY ;
}
else if ( ! Compare ( curAtt - > name , " filter_shape " ) )
{
currentEmber . m_SpatialFilterType = SpatialFilterCreator < T > : : FromString ( string ( attStr ) ) ;
}
else if ( ! Compare ( curAtt - > name , " temporal_filter_type " ) )
{
currentEmber . m_TemporalFilterType = TemporalFilterCreator < T > : : FromString ( string ( attStr ) ) ;
}
else if ( ! Compare ( curAtt - > name , " palette_mode " ) )
{
if ( ! _stricmp ( " step " , attStr ) )
currentEmber . m_PaletteMode = ePaletteMode : : PALETTE_STEP ;
else if ( ! _stricmp ( " linear " , attStr ) )
currentEmber . m_PaletteMode = ePaletteMode : : PALETTE_LINEAR ;
else
{
currentEmber . m_PaletteMode = ePaletteMode : : PALETTE_STEP ;
AddToReport ( string ( loc ) + " : Unrecognized palette mode " + string ( attStr ) + " , using step " ) ;
}
}
else if ( ! Compare ( curAtt - > name , " background " ) )
{
istringstream is ( attStr ) ;
is > > currentEmber . m_Background [ 0 ] //[0..1]
> > currentEmber . m_Background [ 1 ]
> > currentEmber . m_Background [ 2 ] ;
}
else if ( ! Compare ( curAtt - > name , " curves " ) )
{
istringstream is ( attStr ) ;
for ( i = 0 ; i < 4 ; i + + )
{
for ( glm : : length_t j = 0 ; j < 4 ; 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 ] ;
}
}
}
xmlFree ( attStr ) ;
}
//Finished with ember attributes. Now look at the children of the ember element.
for ( childNode = emberNode - > children ; childNode ; childNode = childNode - > next )
{
if ( ! Compare ( childNode - > name , " color " ) )
{
index = - 1 ;
float r = 0 , g = 0 , b = 0 , a = 0 ;
//Loop through the attributes of the color element.
att = childNode - > properties ;
if ( att = = nullptr )
{
AddToReport ( string ( loc ) + " : No attributes for color element " ) ;
continue ;
}
for ( curAtt = att ; curAtt ; curAtt = curAtt - > next )
{
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 " ) )
is > > r > > g > > b ;
else if ( ! Compare ( curAtt - > name , " rgba " ) )
is > > r > > g > > b > > a ;
else if ( ! Compare ( curAtt - > name , " a " ) )
is > > a ;
else
AddToReport ( string ( loc ) + " : Unknown color attribute " + string ( CCX ( curAtt - > name ) ) ) ;
xmlFree ( attStr ) ;
}
//Palette colors are [0..255], convert to [0..1].
if ( index > = 0 & & index < = 255 )
{
float alphaPercent = a / 255.0f ; //Aplha percentage in the range of 0 to 1.
//Premultiply the palette.
currentEmber . m_Palette . m_Entries [ index ] . r = alphaPercent * ( r / 255.0f ) ;
currentEmber . m_Palette . m_Entries [ index ] . g = alphaPercent * ( g / 255.0f ) ;
currentEmber . m_Palette . m_Entries [ index ] . b = alphaPercent * ( b / 255.0f ) ;
currentEmber . m_Palette . m_Entries [ index ] . a = a / 255.0f ; //Will be one for RGB, and other than one if RGBA with A != 255.
}
else
{
stringstream ss ;
ss < < " ParseEmberElement() : Color element with bad/missing index attribute " < < index ;
AddToReport ( ss . str ( ) ) ;
}
}
else if ( ! Compare ( childNode - > name , " colors " ) )
{
//Loop through the attributes of the color element.
att = childNode - > properties ;
if ( att = = nullptr )
{
AddToReport ( string ( loc ) + " : No attributes for colors element " ) ;
continue ;
}
for ( curAtt = att ; curAtt ; curAtt = curAtt - > next )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
if ( ! Compare ( curAtt - > name , " count " ) )
{
Aton ( attStr , count ) ;
}
else if ( ! Compare ( curAtt - > name , " data " ) )
{
if ( ! ParseHexColors ( attStr , currentEmber , count , - 4 ) )
{
AddToReport ( string ( loc ) + " : Error parsing hexformatted colors, some may be set to zero " ) ;
}
}
else
{
AddToReport ( string ( loc ) + " : Unknown color attribute " + string ( CCX ( curAtt - > name ) ) ) ;
}
xmlFree ( attStr ) ;
}
}
else if ( ! Compare ( childNode - > name , " palette " ) )
{
//This could be either the old form of palette or the new form.
//Make sure BOTH are not specified, otherwise either are ok.
int numColors = 0 ;
int numBytes = 0 ;
//Loop through the attributes of the palette element.
att = childNode - > properties ;
if ( att = = nullptr )
{
AddToReport ( string ( loc ) + " : No attributes for palette element " ) ;
continue ;
}
for ( curAtt = att ; curAtt ; curAtt = curAtt - > next )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
if ( ! Compare ( curAtt - > name , " count " ) )
{
Aton ( attStr , numColors ) ;
}
else if ( ! Compare ( curAtt - > name , " format " ) )
{
if ( ! _stricmp ( attStr , " RGB " ) )
numBytes = 3 ;
else if ( ! _stricmp ( attStr , " RGBA " ) )
numBytes = 4 ;
else
{
AddToReport ( string ( loc ) + " : Unrecognized palette format string " + string ( attStr ) + " , defaulting to RGB " ) ;
numBytes = 3 ;
}
}
else if ( ! Compare ( curAtt - > name , " source_colors " ) )
{
string s ( attStr ) ;
auto vec1 = Split ( s , ' ' ) ;
for ( auto & v : vec1 )
{
auto vec2 = Split ( v , ' , ' ) ;
if ( vec2 . size ( ) = = 4 )
{
float d1 = Clamp ( std : : stof ( vec2 [ 0 ] ) , 0.0f , 1.0f ) ;
currentEmber . m_Palette . m_SourceColors [ d1 ] = v4F (
Clamp ( std : : stof ( vec2 [ 1 ] ) , 0.0f , 1.0f ) ,
Clamp ( std : : stof ( vec2 [ 2 ] ) , 0.0f , 1.0f ) ,
Clamp ( std : : stof ( vec2 [ 3 ] ) , 0.0f , 1.0f ) , 1.0f ) ;
}
else
AddToReport ( string ( loc ) + " : Bad palette color source value: " + v ) ;
}
}
else
{
AddToReport ( string ( loc ) + " : Unknown palette attribute " + string ( CCX ( curAtt - > name ) ) ) ;
}
xmlFree ( attStr ) ;
}
//Removing support for whatever "old format" was in flam3.
//Read formatted string from contents of tag.
char * palStr = CX ( xmlNodeGetContent ( childNode ) ) ;
if ( ! ParseHexColors ( palStr , currentEmber , numColors , numBytes ) )
{
AddToReport ( string ( loc ) + " : Problem reading hexadecimal color data in palette " ) ;
}
xmlFree ( palStr ) ;
}
else if ( ! Compare ( childNode - > name , " symmetry " ) )
{
int symKind = INT_MAX ;
//Loop through the attributes of the palette element.
att = childNode - > properties ;
if ( att = = nullptr )
{
AddToReport ( string ( loc ) + " : No attributes for palette element " ) ;
continue ;
}
for ( curAtt = att ; curAtt ; curAtt = curAtt - > next )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
if ( ! Compare ( curAtt - > name , " kind " ) )
{
Aton ( attStr , symKind ) ;
}
else
{
AddToReport ( string ( loc ) + " : Unknown symmetry attribute " + string ( attStr ) ) ;
continue ;
}
xmlFree ( attStr ) ;
}
//if (symKind != INT_MAX)//What to do about this? Should sym not be saved? Or perhaps better intelligence when adding?//TODO//BUG.
//{
// currentEmber.AddSymmetry(symKind, *(GlobalRand.get()));//Determine what to do here.
//}
}
else if ( ! Compare ( childNode - > name , " xform " ) | | ! Compare ( childNode - > name , " finalxform " ) )
{
Xform < T > * theXform = nullptr ;
if ( ! Compare ( childNode - > name , " finalxform " ) )
{
Xform < T > finalXform ;
if ( ! ParseXform ( childNode , finalXform , false , fromEmber ) )
{
AddToReport ( string ( loc ) + " : Error parsing final xform " ) ;
}
else
{
if ( finalXform . m_Weight ! = 0 )
{
finalXform . m_Weight = 0 ;
AddToReport ( string ( loc ) + " : Final xforms should not have weight specified, setting to zero " ) ;
}
currentEmber . SetFinalXform ( finalXform ) ;
theXform = currentEmber . NonConstFinalXform ( ) ;
}
}
else
{
Xform < T > xform ;
if ( ! ParseXform ( childNode , xform , false , fromEmber ) )
{
AddToReport ( string ( loc ) + " : Error parsing xform " ) ;
}
else
{
currentEmber . AddXform ( xform ) ;
theXform = currentEmber . GetXform ( currentEmber . XformCount ( ) - 1 ) ;
}
}
if ( theXform )
{
//Check for non-zero motion params.
if ( fabs ( theXform - > m_MotionFreq ) > 0.0 ) //Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here.
{
AddToReport ( string ( loc ) + " : Motion parameters should not be specified in regular, non-motion xforms " ) ;
}
//Motion Language: Check the xform element for children - should be named 'motion'.
for ( motionNode = childNode - > children ; motionNode ; motionNode = motionNode - > next )
{
if ( ! Compare ( motionNode - > name , " motion " ) )
{
Xform < T > xform ( false ) ; //Will only have valid values in fields parsed for motion, all others will be EMPTYFIELD.
if ( ! ParseXform ( motionNode , xform , true , fromEmber ) )
AddToReport ( string ( loc ) + " : Error parsing motion xform " ) ;
else
theXform - > m_Motion . push_back ( xform ) ;
}
}
}
}
else if ( ! Compare ( childNode - > name , " edit " ) )
{
//Create a new XML document with this edit node as the root node.
currentEmber . m_Edits = xmlNewDoc ( XC ( " 1.0 " ) ) ;
editNode = xmlCopyNode ( childNode , 1 ) ;
xmlDocSetRootElement ( currentEmber . m_Edits , editNode ) ;
}
else if ( ! Compare ( childNode - > name , " flame_motion " ) )
{
EmberMotion < T > motion ;
att = childNode - > properties ;
if ( att = = nullptr )
{
AddToReport ( string ( loc ) + " : <flame_motion> element has no attributes " ) ;
return false ;
}
for ( curAtt = att ; curAtt ; curAtt = curAtt - > next )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
if ( ParseAndAssign ( curAtt - > name , attStr , " motion_frequency " , motion . m_MotionFreq , ret ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " motion_offset " , motion . m_MotionOffset , ret ) ) { }
else if ( ! Compare ( curAtt - > name , " motion_function " ) )
{
string func ( attStr ) ;
if ( func = = " sin " )
motion . m_MotionFunc = eMotion : : MOTION_SIN ;
else if ( func = = " triangle " )
motion . m_MotionFunc = eMotion : : MOTION_TRIANGLE ;
else if ( func = = " hill " )
motion . m_MotionFunc = eMotion : : MOTION_HILL ;
else if ( func = = " saw " )
motion . m_MotionFunc = eMotion : : MOTION_SAW ;
else
{
AddToReport ( string ( loc ) + " : invalid flame motion function " + func + " , setting to sin " ) ;
motion . m_MotionFunc = eMotion : : MOTION_SIN ;
}
}
else if ( ! Compare ( curAtt - > name , " zoom " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_ZOOM , motion ) ;
else if ( ! Compare ( curAtt - > name , " cam_zpos " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_ZPOS , motion ) ;
else if ( ! Compare ( curAtt - > name , " cam_persp " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_PERSPECTIVE , motion ) ;
else if ( ! Compare ( curAtt - > name , " cam_yaw " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_YAW , motion ) ;
else if ( ! Compare ( curAtt - > name , " cam_pitch " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_PITCH , motion ) ;
else if ( ! Compare ( curAtt - > name , " cam_dof " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_DEPTH_BLUR , motion ) ;
else if ( ! Compare ( curAtt - > name , " rotate " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_ROTATE , motion ) ;
else if ( ! Compare ( curAtt - > name , " brightness " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_BRIGHTNESS , motion ) ;
else if ( ! Compare ( curAtt - > name , " gamma " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_GAMMA , motion ) ;
else if ( ! Compare ( curAtt - > name , " gamma_threshold " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_GAMMA_THRESH , motion ) ;
else if ( ! Compare ( curAtt - > name , " highlight_power " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_HIGHLIGHT_POWER , motion ) ;
else if ( ! Compare ( curAtt - > name , " vibrancy " ) )
ret = ret & & AttToEmberMotionFloat ( att , attStr , eEmberMotionParam : : FLAME_MOTION_VIBRANCY , motion ) ;
else if ( ! Compare ( curAtt - > name , " background " ) )
{
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 ) ) ) ;
if ( g ! = 0 )
motion . m_MotionParams . push_back ( MotionParam < T > ( eEmberMotionParam : : FLAME_MOTION_BACKGROUND_G , T ( g ) ) ) ;
if ( b ! = 0 )
motion . m_MotionParams . push_back ( MotionParam < T > ( eEmberMotionParam : : FLAME_MOTION_BACKGROUND_B , T ( b ) ) ) ;
}
else if ( ! Compare ( curAtt - > name , " center " ) )
{
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 ) ) ) ;
if ( cy ! = 0 )
motion . m_MotionParams . push_back ( MotionParam < T > ( eEmberMotionParam : : FLAME_MOTION_CENTER_Y , T ( cy ) ) ) ;
}
else
{
AddToReport ( string ( loc ) + " : Unknown flame motion attribute " + string ( CCX ( curAtt - > name ) ) ) ;
}
xmlFree ( attStr ) ;
}
currentEmber . m_EmberMotionElements . push_back ( motion ) ;
}
}
if ( ! fromEmber & & ! newLinear )
currentEmber . Flatten ( m_FlattenNames ) ;
for ( i = 0 ; i < currentEmber . XformCount ( ) ; i + + )
if ( soloXform > = 0 & & i ! = soloXform )
currentEmber . GetXform ( i ) - > m_Opacity = 0 ; //Will calc the cached adjusted viz value later.
return true ;
}
/// <summary>
/// Parse a floating point value from an xml attribute and add the value to a EmberMotion object
/// </summary>
/// <param name="att">The current attribute</param>
/// <param name="attStr">The attribute value to parse</param>
/// <param name="param">The flame motion parameter type</param>
/// <param name="motion">The flame motion element to add the parameter to</param>
/// <returns>True if there were no errors, else false.</returns>
template < typename T >
bool XmlToEmber < T > : : AttToEmberMotionFloat ( xmlAttrPtr att , const char * attStr , eEmberMotionParam param , EmberMotion < T > & motion )
{
const char * loc = __FUNCTION__ ;
bool r = false ;
T val = 0.0 ;
if ( Aton ( attStr , val ) )
{
motion . m_MotionParams . push_back ( MotionParam < T > ( param , val ) ) ;
r = true ;
}
else
{
AddToReport ( string ( loc ) + " : Failed to parse float value for flame motion attribute \" " + string ( CCX ( att - > name ) ) + " \" : " + string ( attStr ) ) ;
}
return r ;
}
/// <summary>
/// Parse an xform element.
/// </summary>
/// <param name="childNode">The current node to parse</param>
/// <param name="xform">The newly constructed xform based on what was parsed</param>
/// <param name="motion">True if this xform is a motion within a parent xform, else false</param>
/// <returns>True if there were no errors, else false.</returns>
template < typename T >
bool XmlToEmber < T > : : ParseXform ( xmlNode * childNode , Xform < T > & xform , bool motion , bool fromEmber )
{
bool success = true ;
char * attStr ;
const char * loc = __FUNCTION__ ;
size_t j ;
T temp ;
double a , b , c , d , e , f ;
xmlAttrPtr attPtr , curAtt ;
//Loop through the attributes of the xform element.
attPtr = childNode - > properties ;
if ( attPtr = = nullptr )
{
AddToReport ( string ( loc ) + " : Error: No attributes for element " ) ;
return false ;
}
for ( curAtt = attPtr ; curAtt ; curAtt = curAtt - > next )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
//First parse out simple float reads.
if ( ParseAndAssign ( curAtt - > name , attStr , " weight " , xform . m_Weight , success ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " color_speed " , xform . m_ColorSpeed , success ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " animate " , xform . m_Animate , success ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " opacity " , xform . m_Opacity , success ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " var_color " , xform . m_DirectColor , success ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " motion_frequency " , xform . m_MotionFreq , success ) ) { }
else if ( ParseAndAssign ( curAtt - > name , attStr , " motion_offset " , xform . m_MotionOffset , success ) ) { }
//Parse more complicated reads that have multiple possible values.
else if ( ! Compare ( curAtt - > name , " name " ) )
{
xform . m_Name = string ( attStr ) ;
std : : replace ( xform . m_Name . begin ( ) , xform . m_Name . end ( ) , ' ' , ' _ ' ) ;
}
else if ( ! Compare ( curAtt - > name , " symmetry " ) ) //Legacy support.
{
//Deprecated, set both color_speed and animate to this value.
//Huh? Either set it or not?
Aton ( attStr , temp ) ;
xform . m_ColorSpeed = ( 1 - temp ) / 2 ;
xform . m_Animate = T ( temp > 0 ? 0 : 1 ) ;
}
else if ( ! Compare ( curAtt - > name , " motion_function " ) )
{
if ( ! _stricmp ( " sin " , attStr ) )
xform . m_MotionFunc = eMotion : : MOTION_SIN ;
else if ( ! _stricmp ( " triangle " , attStr ) )
xform . m_MotionFunc = eMotion : : MOTION_TRIANGLE ;
else if ( ! _stricmp ( " hill " , attStr ) )
xform . m_MotionFunc = eMotion : : MOTION_HILL ;
else if ( ! _stricmp ( " saw " , attStr ) )
xform . m_MotionFunc = eMotion : : MOTION_SAW ;
else
{
xform . m_MotionFunc = eMotion : : MOTION_SIN ;
AddToReport ( string ( loc ) + " : Unknown motion function " + string ( attStr ) + " , using sin " ) ;
}
}
else if ( ! Compare ( curAtt - > name , " color " ) )
{
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 " ) )
{
istringstream is ( attStr ) ;
j = 0 ;
while ( is > > temp )
{
xform . SetXaos ( j , temp ) ;
j + + ;
}
}
else if ( ! Compare ( curAtt - > name , " plotmode " ) )
{
if ( motion = = 1 )
{
AddToReport ( string ( loc ) + " : Motion element cannot have a plotmode attribute " ) ;
}
else if ( ! _stricmp ( " off " , attStr ) )
xform . m_Opacity = 0 ;
}
else if ( ! Compare ( curAtt - > name , " coefs " ) )
{
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 ) ) ;
xform . m_Affine . D ( T ( d ) ) ;
xform . m_Affine . E ( T ( e ) ) ;
xform . m_Affine . F ( T ( f ) ) ;
}
else if ( ! Compare ( curAtt - > name , " post " ) )
{
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 ) ) ;
xform . m_Post . D ( T ( d ) ) ;
xform . m_Post . E ( T ( e ) ) ;
xform . m_Post . F ( T ( f ) ) ;
}
else
{
//Only correct names if it came from an outside source. Names originating from this library are always considered correct.
string s = fromEmber ? string ( CCX ( curAtt - > name ) ) : GetCorrectedVariationName ( m_BadVariationNames , curAtt ) ;
if ( auto var = m_VariationList - > GetVariation ( s ) )
{
T weight = 0 ;
Aton ( attStr , weight ) ;
if ( ! IsNearZero ( weight ) ) //Having a variation with zero weight makes no sense, so guard against it.
{
auto varCopy = var - > Copy ( ) ;
varCopy - > m_Weight = weight ;
xform . AddVariation ( varCopy ) ;
}
}
//else
//{
// AddToReport("Unsupported variation: " + string((const char*)curAtt->name));
//}
}
xmlFree ( attStr ) ;
}
//Handle var1.
for ( curAtt = attPtr ; curAtt ; curAtt = curAtt - > next ) //Legacy fields, most likely not used.
{
bool var1 = false ;
if ( ! Compare ( curAtt - > name , " var1 " ) )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
for ( j = 0 ; j < xform . TotalVariationCount ( ) ; j + + )
xform . GetVariation ( j ) - > m_Weight = 0 ;
if ( Aton ( attStr , temp ) )
{
uint iTemp = static_cast < uint > ( temp ) ;
if ( iTemp < xform . TotalVariationCount ( ) )
{
xform . GetVariation ( iTemp ) - > m_Weight = 1 ;
var1 = true ;
}
}
if ( ! var1 )
AddToReport ( string ( loc ) + " : Bad value for var1 " + string ( attStr ) ) ;
xmlFree ( attStr ) ;
break ;
}
}
//Handle var.
for ( curAtt = attPtr ; curAtt ; curAtt = curAtt - > next ) //Legacy fields, most likely not used.
{
bool var = false ;
if ( ! Compare ( curAtt - > name , " var " ) )
{
attStr = reinterpret_cast < char * > ( xmlGetProp ( childNode , curAtt - > name ) ) ;
if ( Aton ( attStr , temp ) )
{
for ( j = 0 ; j < xform . TotalVariationCount ( ) ; j + + )
xform . GetVariation ( j ) - > m_Weight = temp ;
var = true ;
}
if ( ! var )
AddToReport ( string ( loc ) + " : Bad value for var " + string ( attStr ) ) ;
xmlFree ( attStr ) ;
break ;
}
}
//Now that all xforms have been parsed, go through and try to find params for the parametric variations.
for ( size_t i = 0 ; i < xform . TotalVariationCount ( ) ; i + + )
{
if ( ParametricVariation < T > * parVar = dynamic_cast < ParametricVariation < T > * > ( xform . GetVariation ( i ) ) )
{
for ( curAtt = attPtr ; curAtt ; curAtt = curAtt - > next )
{
//Only correct names if it came from an outside source. Names originating from this library are always considered correct.
string s = fromEmber ? string ( CCX ( curAtt - > name ) ) : GetCorrectedParamName ( m_BadParamNames , CCX ( curAtt - > name ) ) ;
const char * name = s . c_str ( ) ;
if ( parVar - > ContainsParam ( name ) )
{
T val = 0 ;
attStr = CX ( xmlGetProp ( childNode , curAtt - > name ) ) ;
if ( Aton ( attStr , val ) )
{
parVar - > SetParamVal ( name , val ) ;
}
else
{
AddToReport ( string ( loc ) + " : Failed to parse parametric variation parameter " + s + " - " + string ( attStr ) ) ;
}
xmlFree ( attStr ) ;
}
}
}
}
return true ;
}
/// <summary>
/// Some Apophysis plugins use an inconsistent naming scheme for the parametric variation variables.
/// This function identifies and converts them to Ember's consistent naming convention.
/// </summary>
/// <param name="names">The map of corrected names to search</param>
/// <param name="name">The current Xml node to check</param>
/// <returns>The corrected name if one was found, else the passed in name.</returns>
template < typename T >
string XmlToEmber < T > : : GetCorrectedParamName ( const unordered_map < string , string > & names , const char * name )
{
const auto & newName = names . find ( ToLower ( name ) ) ;
if ( newName ! = names . end ( ) )
return newName - > second ;
else
return name ;
}
/// <summary>
/// Some Apophysis plugins use an inconsistent naming scheme for variation names.
/// This function identifies and converts them to Ember's consistent naming convention.
/// It uses some additional intelligence to ensure the variation is the expected one,
/// by examining the rest of the xform for the existence of parameter names.
/// </summary>
/// <param name="vec">The vector of corrected names to search</param>
/// <param name="att">The current Xml node to check</param>
/// <returns>The corrected name if one was found, else the passed in name.</returns>
template < typename T >
string XmlToEmber < T > : : GetCorrectedVariationName ( vector < pair < pair < string , string > , vector < string > > > & vec , xmlAttrPtr att )
{
for ( auto & v : vec )
{
if ( ! _stricmp ( v . first . first . c_str ( ) , CCX ( att - > name ) ) ) //Do case insensitive here.
{
if ( ! v . second . empty ( ) )
{
for ( size_t j = 0 ; j < v . second . size ( ) ; j + + )
{
if ( XmlContainsTag ( att , v . second [ j ] . c_str ( ) ) )
return v . first . second ;
}
}
else
{
return v . first . second ;
}
}
}
return string ( CCX ( att - > name ) ) ;
}
/// <summary>
/// Determine if an Xml node contains a given tag.
/// </summary>
/// <param name="att">The Xml node to search</param>
/// <param name="name">The node name to search for</param>
/// <returns>True if name was found, else false.</returns>
template < typename T >
bool XmlToEmber < T > : : XmlContainsTag ( xmlAttrPtr att , const char * name )
{
xmlAttrPtr temp = att ;
do
{
if ( ! _stricmp ( name , CCX ( temp - > name ) ) )
return true ;
}
while ( ( temp = temp - > next ) ) ;
return false ;
}
/// <summary>
/// Parse hexadecimal colors.
/// This can read RGB and RGBA, however only RGB will be stored.
/// </summary>
/// <param name="colstr">The string of hex colors to parse</param>
/// <param name="ember">The ember whose palette will be assigned the colors</param>
/// <param name="numColors">The number of colors present</param>
/// <param name="chan">The number of channels in each color</param>
/// <returns>True if there were no errors, else false.</returns>
template < typename T >
bool XmlToEmber < T > : : ParseHexColors ( char * colstr , Ember < T > & ember , size_t numColors , intmax_t chan )
{
stringstream ss , temp ( colstr ) ; ss > > std : : hex ;
string s1 , s ;
size_t tmp , colorCount = 0 ;
s . reserve ( 1536 ) ;
while ( temp > > s1 )
s + = s1 ;
auto length = s . size ( ) ;
for ( size_t strIndex = 0 ; strIndex < length ; )
{
for ( glm : : length_t i = 0 ; i < 3 & & colorCount < ember . m_Palette . Size ( ) ; i + + )
{
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 ] = float ( tmp ) / 255.0f ; //Hex palette is [0..255], convert to [0..1].
}
ember . m_Palette . m_Entries [ colorCount ] [ 3 ] = float ( 1 ) ;
colorCount + + ;
}
return length > = 256 ;
}
/// <summary>
/// Wrapper to parse a numeric Xml string value and convert it.
/// </summary>
/// <param name="name">The xml tag to parse</param>
/// <param name="attStr">The name of the Xml attribute</param>
/// <param name="str">The name of the Xml tag</param>
/// <param name="val">The parsed value</param>
/// <param name="b">Bitwise ANDed with true if name matched str and the conversion succeeded, else false. Used for keeping a running value between successive calls.</param>
/// <returns>True if the tag was matched and the conversion succeeded, else false</returns>
template < typename T >
template < typename valT >
bool XmlToEmber < T > : : ParseAndAssign ( const xmlChar * name , const char * attStr , const char * str , valT & val , bool & b )
{
bool ret = false ;
if ( ! Compare ( name , str ) )
{
istringstream istr ( attStr ) ;
istr > > val ;
ret = ! istr . bad ( ) & & ! istr . fail ( ) ; //Means the Compare() was right, and the conversion succeeded.
}
return ret ;
}
//This class had to be implemented in a cpp file because the compiler was breaking.
//So the explicit instantiation must be declared here rather than in Ember.cpp where
//all of the other classes are done.
# define EXPORT_XML_TO_EMBER(T) \
template EMBER_API class XmlToEmber < T > ; \
template EMBER_API bool XmlToEmber < T > : : Parse ( byte * , const char * , list < Ember < T > > & , bool ) ; \
template EMBER_API bool XmlToEmber < T > : : Parse ( byte * , const char * , vector < Ember < T > > & , bool ) ; \
template EMBER_API bool XmlToEmber < T > : : Parse ( const char * , list < Ember < T > > & , bool ) ; \
template EMBER_API bool XmlToEmber < T > : : Parse ( const char * , vector < Ember < T > > & , bool ) ; \
template EMBER_API bool XmlToEmber < T > : : Aton ( const char * , size_t & ) ;
EXPORT_XML_TO_EMBER ( float )
# ifdef DO_DOUBLE
EXPORT_XML_TO_EMBER ( double )
# endif
}