// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once #include #include #include #include #include #include #include #include #include #if MAX_RELEASE >= 25000 #include #else #include #endif // The operators get defined in the global namespace // ---- Output operators inline std::ostream& operator<<( std::ostream& out, const Point3& pt ) { out << "[" << pt.x << "," << pt.y << "," << pt.z << "]"; return out; } // TODO: In max 6, calling this operator causes a crash. A confusing crash, because it never actually // enters this function. inline std::ostream& operator<<( std::ostream& out, const IPoint3& pt ) { /* TraceFn(); TraceVar( &out ); TraceVar( &pt ); TraceVar( pt.x ); TraceVar( pt.y ); TraceVar( pt.z ); */ out << "[" << pt.x << "," << pt.y << "," << pt.z << "]"; // out << "!!!???!!!"; return out; } // inline std::string toString(const Matrix3 & mat) //{ // ostringstream ostr; // ostr << mat; // return ostr.str(); // } inline std::ostream& operator<<( std::ostream& out, const Box3& box ) { Point3 min = box.Min(); Point3 max = box.Max(); out << "[" << min.x << "," << min.y << "," << min.z << "]-"; out << "[" << max.x << "," << max.y << "," << max.z << "]"; return out; } inline std::ostream& operator<<( std::ostream& out, const Matrix3& mat ) { out << "matrix [ "; for( int r = 0; r < 4; ++r ) { Point3 row = mat.GetRow( r ); out << row.x << " " << row.y << " " << row.z << " "; } out << "]"; return out; } inline std::string toMaxscriptFormat( const Matrix3& mat ) { std::stringstream ss; ss << "Matrix3"; for( int r = 0; r < 4; ++r ) { Point3 row = mat.GetRow( r ); ss << " [" << row.x << ", " << row.y << ", " << row.z << "]"; } return ss.str(); } inline void write_TimeValue( std::ostream& out, TimeValue t ) { if( t == TIME_NegInfinity ) out << "-inf"; else if( t == TIME_PosInfinity ) out << "+inf"; else out << float( t ) / TIME_TICKSPERSEC << 's'; } inline std::ostream& operator<<( std::ostream& out, const Interval& ivl ) { if( ivl == FOREVER ) out << "[ FOREVER ]"; else if( ivl == NEVER ) out << "[ NEVER ]"; else { out << "[ "; write_TimeValue( out, ivl.Start() ); out << ", "; write_TimeValue( out, ivl.End() ); out << " ]"; } return out; } inline std::ostream& operator<<( std::ostream& out, const Class_ID& cid ) { std::stringstream stmp; stmp << std::hex; stmp << "Class_ID(0x" << const_cast( &cid )->PartA(); stmp << ", 0x" << const_cast( &cid )->PartB() << ")"; out << stmp.str(); return out; } // ----- Input operators inline std::istream& operator>>( std::istream& in, Point3& pt ) { char ch; in >> ch; if( ch == '[' || ch == '(' ) { in >> pt.x; if( !in.good() ) return in; in >> ch; if( in.good() && ch != ',' ) in.putback( ch ); in >> pt.y; if( !in.good() ) return in; in >> ch; if( in.good() && ch != ',' ) in.putback( ch ); in >> pt.z; if( !in.good() ) return in; in >> ch; // if( in != ')' && in != ']' ) {} } else { in.putback( ch ); in.setstate( ::std::ios_base::failbit ); } return in; } inline std::istream& operator>>( std::istream& in, IPoint3& pt ) { char ch; in >> ch; if( ch == '[' || ch == '(' ) { in >> pt.x; if( !in.good() ) return in; in >> ch; if( in.good() && ch != ',' ) in.putback( ch ); in >> pt.y; if( !in.good() ) return in; in >> ch; if( in.good() && ch != ',' ) in.putback( ch ); in >> pt.z; if( !in.good() ) return in; in >> ch; // if( in != ')' && in != ']' ) {} } else { in.putback( ch ); in.setstate( ::std::ios_base::failbit ); } return in; } namespace frantic { namespace max3d { namespace detail { enum { kNameChunk = 5, // This is arbitrary kValueChunk, }; #if MAX_VERSION_MAJOR >= 15 // Starting in 3ds Max 2013 (MAX_VERSION_MAJOR 15), ISave and ILoad have a // CodePage() function that indicates which code page should be used to // save or load Unicode strings. // The following to_string() and to_tstring() functions are used to convert // to and from the CodePage(). inline std::string to_max_file_string( const std::wstring& s, ISave* isave ) { return TSTR::FromUTF16( s.c_str() ).ToCP( isave->CodePage() ).data(); } inline std::wstring tstring_from_max_file_string( const std::string& s, ILoad* iload ) { return TSTR::FromCP( iload->CodePage(), s.c_str() ).ToUTF16().data(); } #else // In earlier versions of 3ds Max there is no CodePage() member, so we // pass the string through unchanged. inline const std::string& to_max_file_string( const std::string& s, ISave* /*isave*/ ) { return s; } inline const std::string& tstring_from_max_file_string( const std::string& s, ILoad* /*load*/ ) { return s; } #endif inline IOResult isave_write_prop( ISave* isave, const std::map::value_type& val ) { IOResult result; isave->BeginChunk( kNameChunk ); result = isave->WriteCString( val.first.c_str() ); if( result != IO_OK ) return result; isave->EndChunk(); isave->BeginChunk( kValueChunk ); result = const_cast( val.second ).Save( isave ); if( result != IO_OK ) return result; isave->EndChunk(); return IO_OK; } inline IOResult isave_write_prop( ISave* isave, const FPValue& val ) { IOResult result; result = const_cast( val ).Save( isave ); if( result != IO_OK ) return result; return IO_OK; } template struct iload_read_propmap_impl { inline static IOResult apply( ILoad* iload, Container& outContainer ) { IOResult result; FPValue val; result = iload->OpenChunk(); while( result == IO_OK ) { result = val.Load( iload ); if( result != IO_OK ) return IO_ERROR; outContainer.push_back( val ); iload->CloseChunk(); result = iload->OpenChunk(); } if( result != IO_END ) return IO_ERROR; return IO_OK; } }; template