<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2016, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "boost/interprocess/smart_ptr/unique_ptr.hpp"

#include "IECore/MessageHandler.h"
#include "IECore/SimpleTypedData.h"
#include "IECore/DespatchTypedData.h"

#include "IECoreArnold/ParameterAlgo.h"

using namespace std;
using namespace IECore;
using namespace IECoreArnold;

//////////////////////////////////////////////////////////////////////////
// Internal utilities
//////////////////////////////////////////////////////////////////////////

namespace
{

typedef boost::interprocess::unique_ptr<AtArray, void (*)( AtArray *)> ArrayPtr;

template<typename T>
inline const T *dataCast( const char *name, const IECore::Data *data )
{
	const T *result = runTimeCast<const T>( data );
	if( result )
	{
		return result;
	}
	msg( Msg::Warning, "setParameter", boost::format( "Unsupported value type \"%s\" for parameter \"%s\" (expected %s)." ) % data->typeName() % name % T::staticTypeName() );
	return NULL;
}

void setParameterInternal( AtNode *node, const char *name, int parameterType, bool array, const IECore::Data *value )
{
	if( array )
	{
		AtArray *a = ParameterAlgo::dataToArray( value );
		if( !a )
		{
			msg( Msg::Warning, "setParameter", boost::format( "Unable to create array from data of type \"%s\" for parameter \"%s\"" ) % value->typeName() % name );
			return;
		}
		if( a->type != parameterType )
		{
			msg( Msg::Warning, "setParameter", boost::format( "Unable to create array of type %s from data of type \"%s\" for parameter \"%s\"" ) % AiParamGetTypeName( parameterType ) % value->typeName() % name );
			return;
		}
		AiNodeSetArray( node, name, a );
	}
	else
	{
		switch( parameterType )
		{
			case AI_TYPE_INT :
				if( const IntData *data = dataCast<IntData>( name, value ) )
				{
					AiNodeSetInt( node, name, data->readable() );
				}
				break;
			case AI_TYPE_BYTE :
				if( const IntData *data = dataCast<IntData>( name, value ) )
				{
					AiNodeSetByte( node, name, data->readable() );
				}
				break;
			case AI_TYPE_FLOAT :
				if( const FloatData *data = dataCast<FloatData>( name, value ) )
				{
					AiNodeSetFlt( node, name, data->readable() );
				}
				break;
			case AI_TYPE_STRING :
				if( const StringData *data = dataCast<StringData>( name, value ) )
				{
					AiNodeSetStr( node, name, data->readable().c_str() );
				}
				break;
			case AI_TYPE_RGB :
				if( const Color3fData *data = dataCast<Color3fData>( name, value ) )
				{
					const Imath::Color3f &c = data->readable();
					AiNodeSetRGB( node, name, c[0], c[1], c[2] );
				}
				break;
			case AI_TYPE_RGBA :
				if( const Color4fData *data = dataCast<Color4fData>( name, value ) )
				{
					const Imath::Color4f &c = data->readable();
					AiNodeSetRGBA( node, name, c[0], c[1], c[2], c[3] );
				}
				break;
			case AI_TYPE_ENUM :
				if( const StringData *data = dataCast<StringData>( name, value ) )
				{
					AiNodeSetStr( node, name, data->readable().c_str() );
				}
				break;
			case AI_TYPE_BOOLEAN :
				if( const BoolData *data = dataCast<BoolData>( name, value ) )
				{
					AiNodeSetBool( node, name, data->readable() );
				}
				break;
			case AI_TYPE_POINT2 :
				if( const V2fData *data = dataCast<V2fData>( name, value ) )
				{
					const Imath::V2f &v = data->readable();
					AiNodeSetPnt2( node, name, v.x, v.y );
				}
				break;
			default :
			{
				msg( Msg::Warning, "setParameter", boost::format( "Arnold parameter \"%s\" has unsupported type \"%s\"." ) % name % AiParamGetTypeName( parameterType ) );
			}
		}
	}
}

template<typename T, typename F>
IECore::DataPtr arrayToDataInternal( AtArray *array, F f )
{
	typedef vector<T> VectorType;
	typedef IECore::TypedData<vector<T> > DataType;
	typename DataType::Ptr data = new DataType;
	VectorType &v = data->writable();

	v.reserve( array->nelements );
	for( size_t i = 0; i < array->nelements; ++i )
	{
		v.push_back( f( array, i, __AI_FILE__, __AI_LINE__ ) );
	}

	return data;
}

/// \todo Flesh this out to support more types and then
/// consider exposing it in the public API.
IECore::DataPtr arrayToData( AtArray *array )
{
	if( array->nkeys > 1 )
	{
		/// \todo Decide how to deal with more
		/// than one key - is it more useful to return multiple Data
		/// objects or to put it all in one?
		return NULL;
	}

	switch( array->type )
	{
		case AI_TYPE_BOOLEAN :
			return arrayToDataInternal<bool>( array, AiArrayGetBoolFunc );
		case AI_TYPE_INT :
			return arrayToDataInternal<int>( array, AiArrayGetIntFunc );
		case AI_TYPE_FLOAT :
			return arrayToDataInternal<float>( array, AiArrayGetFltFunc );
		case AI_TYPE_STRING :
			return arrayToDataInternal<string>( array, AiArrayGetStrFunc );
		default :
			return NULL;
	}
}

IECore::DataPtr getParameterInternal( AtNode *node, const char *name, int parameterType )
{
	switch( parameterType )
	{
		case AI_TYPE_BOOLEAN :
			return new BoolData( AiNodeGetBool( node, name ) );
		case AI_TYPE_INT :
			return new IntData( AiNodeGetInt( node, name ) );
		case AI_TYPE_FLOAT :
			return new FloatData( AiNodeGetFlt( node, name ) );
		case AI_TYPE_STRING :
			return new StringData( AiNodeGetStr( node, name ) );
		case AI_TYPE_ARRAY :
			return arrayToData( AiNodeGetArray( node, name ) );
	}
	return NULL;
}

} // namespace

//////////////////////////////////////////////////////////////////////////
// Implementation of public API.
//////////////////////////////////////////////////////////////////////////

namespace IECoreArnold
{

namespace ParameterAlgo
{

void setParameter( AtNode *node, const AtParamEntry *parameter, const IECore::Data *value )
{
	bool isArray = false;
	int type = AiParamGetType( parameter );
	if( type == AI_TYPE_ARRAY )
	{
		type = AiParamGetDefault( parameter )->ARRAY->type;
		isArray = true;
	}

	setParameterInternal( node, AiParamGetName( parameter ), type, isArray, value );
}

void setParameter( AtNode *node, const char *name, const IECore::Data *value )
{
	const AtParamEntry *parameter = AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), name );
	if( parameter )
	{
		setParameter( node, parameter, value );
	}
	else
	{
		bool array = false;
		int type = parameterType( value->typeId(), array );
		if( type != AI_TYPE_NONE )
		{
			std::string typeString = "constant ";
			if( array )
			{
				typeString += "ARRAY ";
			}
			typeString += AiParamGetTypeName( type );
			AiNodeDeclare( node, name, typeString.c_str() );
			setParameterInternal( node, name, type, array, value );
		}
		else
		{
			msg(
				Msg::Warning,
				"setParameter",
				boost::format( "Unsupported data type \"%s\" for name \"%s\"" ) % value->typeName() % name
			);
		}
	}
}

void setParameters( AtNode *node, const IECore::CompoundDataMap &values )
{
	for( CompoundDataMap::const_iterator it=values.begin(); it!=values.end(); it++ )
	{
		setParameter( node, it->first.value().c_str(), it->second.get() );
	}
}

IECore::DataPtr getParameter( AtNode *node, const AtParamEntry *parameter )
{
	return getParameterInternal( node, AiParamGetName( parameter ), AiParamGetType( parameter ) );
}

IECore::DataPtr getParameter( AtNode *node, const AtUserParamEntry *parameter )
{
	return getParameterInternal( node, AiUserParamGetName( parameter ), AiUserParamGetType( parameter ) );
}

IECore::DataPtr getParameter( AtNode *node, const char *name )
{
	const AtParamEntry *parameter = AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), name );
	if( parameter )
	{
		return getParameter( node, parameter );
	}
	else
	{
		const AtUserParamEntry *userParameter = AiNodeLookUpUserParameter( node, name );
		if( userParameter )
		{
			return getParameter( node, userParameter );
		}
	}

	return NULL;
}

void getParameters( AtNode *node, IECore::CompoundDataMap &values )
{
	/// \todo Non-user parameters

	AtUserParamIterator *it = AiNodeGetUserParamIterator( node );
	while( const AtUserParamEntry *param = AiUserParamIteratorGetNext( it ) )
	{
		DataPtr d = getParameter( node, param );
		if( d )
		{
			values[AiUserParamGetName( param )] = d;
		}
		else
		{
			msg(
				Msg::Warning,
				"getParameters",
				boost::format( "Unable to convert user parameter \"%s\"" ) % AiUserParamGetName( param )
			);
		}
	}
	AiUserParamIteratorDestroy( it );
}

int parameterType( IECore::TypeId dataType, bool &array )
{
	switch( dataType )
	{
		// non-array types

		case IntDataTypeId :
			array = false;
			return AI_TYPE_INT;
		case FloatDataTypeId :
			array = false;
			return AI_TYPE_FLOAT;
		case StringDataTypeId :
			array = false;
			return AI_TYPE_STRING;
		case Color3fDataTypeId :
			array = false;
			return AI_TYPE_RGB;
		case BoolDataTypeId :
			array = false;
			return AI_TYPE_BOOLEAN;

		// array types

		case IntVectorDataTypeId :
			array = true;
			return AI_TYPE_INT;
		case FloatVectorDataTypeId :
			array = true;
			return AI_TYPE_FLOAT;
		case StringVectorDataTypeId :
			array = true;
			return AI_TYPE_STRING;
		case Color3fVectorDataTypeId :
			array = true;
			return AI_TYPE_RGB;
		case BoolVectorDataTypeId :
			array = true;
			return AI_TYPE_BOOLEAN;
		case V3fVectorDataTypeId :
			array = true;
			return AI_TYPE_VECTOR;
		default :
			return AI_TYPE_NONE;
	}
}

AtArray *dataToArray( const IECore::Data *data, int aiType )
{
	if( aiType == AI_TYPE_NONE )
	{
		bool isArray = false;
		aiType = parameterType( data->typeId(), isArray );
		if( aiType == AI_TYPE_NONE || !isArray )
		{
			return NULL;
		}
	}

	const void *dataAddress = despatchTypedData<TypedDataAddress, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( data ) );
	size_t dataSize = despatchTypedData<TypedDataSize, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( data ) );
	return AiArrayConvert( dataSize, 1, aiType, dataAddress );
}

IECOREARNOLD_API AtArray *dataToArray( const std::vector<const IECore::Data *> &samples, int aiType )
{
	if( aiType == AI_TYPE_NONE )
	{
		bool isArray = false;
		aiType = parameterType( samples.front()->typeId(), isArray );
		if( aiType == AI_TYPE_NONE || !isArray )
		{
			return NULL;
		}
	}

	size_t arraySize = despatchTypedData<TypedDataSize, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( samples.front() ) );
	ArrayPtr array(
		AiArrayAllocate( arraySize, samples.size(), aiType ),
		AiArrayDestroy
	);

	for( vector<const IECore::Data *>::const_iterator it = samples.begin(), eIt = samples.end(); it != eIt; ++it )
	{
		if( (*it)->typeId() != samples.front()->typeId() )
		{
			throw IECore::Exception( "ParameterAlgo::dataToArray() : Mismatched sample types." );
		}
		const size_t dataSize = despatchTypedData<TypedDataSize, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( *it ) );
		if( dataSize != arraySize )
		{
			throw IECore::Exception( "ParameterAlgo::dataToArray() : Mismatched sample lengths." );
		}
		const void *dataAddress = despatchTypedData<TypedDataAddress, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( *it ) );
		AiArraySetKey( array.get(), /* key = */ it - samples.begin(), dataAddress );
	}

	return array.release();
}

} // namespace ParameterAlgo

} // namespace IECoreArnold
<commit_msg>IECoreArnold : Added support for matrix parameters<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2016, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "boost/interprocess/smart_ptr/unique_ptr.hpp"

#include "IECore/MessageHandler.h"
#include "IECore/SimpleTypedData.h"
#include "IECore/DespatchTypedData.h"

#include "IECoreArnold/ParameterAlgo.h"

using namespace std;
using namespace IECore;
using namespace IECoreArnold;

//////////////////////////////////////////////////////////////////////////
// Internal utilities
//////////////////////////////////////////////////////////////////////////

namespace
{

typedef boost::interprocess::unique_ptr<AtArray, void (*)( AtArray *)> ArrayPtr;

template<typename T>
inline const T *dataCast( const char *name, const IECore::Data *data )
{
	const T *result = runTimeCast<const T>( data );
	if( result )
	{
		return result;
	}
	msg( Msg::Warning, "setParameter", boost::format( "Unsupported value type \"%s\" for parameter \"%s\" (expected %s)." ) % data->typeName() % name % T::staticTypeName() );
	return NULL;
}

void setParameterInternal( AtNode *node, const char *name, int parameterType, bool array, const IECore::Data *value )
{
	if( array )
	{
		AtArray *a = ParameterAlgo::dataToArray( value );
		if( !a )
		{
			msg( Msg::Warning, "setParameter", boost::format( "Unable to create array from data of type \"%s\" for parameter \"%s\"" ) % value->typeName() % name );
			return;
		}
		if( a->type != parameterType )
		{
			msg( Msg::Warning, "setParameter", boost::format( "Unable to create array of type %s from data of type \"%s\" for parameter \"%s\"" ) % AiParamGetTypeName( parameterType ) % value->typeName() % name );
			return;
		}
		AiNodeSetArray( node, name, a );
	}
	else
	{
		switch( parameterType )
		{
			case AI_TYPE_INT :
				if( const IntData *data = dataCast<IntData>( name, value ) )
				{
					AiNodeSetInt( node, name, data->readable() );
				}
				break;
			case AI_TYPE_BYTE :
				if( const IntData *data = dataCast<IntData>( name, value ) )
				{
					AiNodeSetByte( node, name, data->readable() );
				}
				break;
			case AI_TYPE_FLOAT :
				if( const FloatData *data = dataCast<FloatData>( name, value ) )
				{
					AiNodeSetFlt( node, name, data->readable() );
				}
				break;
			case AI_TYPE_STRING :
				if( const StringData *data = dataCast<StringData>( name, value ) )
				{
					AiNodeSetStr( node, name, data->readable().c_str() );
				}
				break;
			case AI_TYPE_RGB :
				if( const Color3fData *data = dataCast<Color3fData>( name, value ) )
				{
					const Imath::Color3f &c = data->readable();
					AiNodeSetRGB( node, name, c[0], c[1], c[2] );
				}
				break;
			case AI_TYPE_RGBA :
				if( const Color4fData *data = dataCast<Color4fData>( name, value ) )
				{
					const Imath::Color4f &c = data->readable();
					AiNodeSetRGBA( node, name, c[0], c[1], c[2], c[3] );
				}
				break;
			case AI_TYPE_ENUM :
				if( const StringData *data = dataCast<StringData>( name, value ) )
				{
					AiNodeSetStr( node, name, data->readable().c_str() );
				}
				break;
			case AI_TYPE_BOOLEAN :
				if( const BoolData *data = dataCast<BoolData>( name, value ) )
				{
					AiNodeSetBool( node, name, data->readable() );
				}
				break;
			case AI_TYPE_POINT2 :
				if( const V2fData *data = dataCast<V2fData>( name, value ) )
				{
					const Imath::V2f &v = data->readable();
					AiNodeSetPnt2( node, name, v.x, v.y );
				}
				break;
			case AI_TYPE_MATRIX :
				if( const M44fData *data = dataCast<M44fData>( name, value ) )
				{
					const Imath::M44f &v = data->readable();

					// Can't see any reason why AiNodeSetMatrix couldn't have been declared const,
					// this const_cast seems safe
					AiNodeSetMatrix( node, name, const_cast<float (*)[4]>( v.x ) );
				}
				break;
			default :
			{
				msg( Msg::Warning, "setParameter", boost::format( "Arnold parameter \"%s\" has unsupported type \"%s\"." ) % name % AiParamGetTypeName( parameterType ) );
			}
		}
	}
}

template<typename T, typename F>
IECore::DataPtr arrayToDataInternal( AtArray *array, F f )
{
	typedef vector<T> VectorType;
	typedef IECore::TypedData<vector<T> > DataType;
	typename DataType::Ptr data = new DataType;
	VectorType &v = data->writable();

	v.reserve( array->nelements );
	for( size_t i = 0; i < array->nelements; ++i )
	{
		v.push_back( f( array, i, __AI_FILE__, __AI_LINE__ ) );
	}

	return data;
}

/// \todo Flesh this out to support more types and then
/// consider exposing it in the public API.
IECore::DataPtr arrayToData( AtArray *array )
{
	if( array->nkeys > 1 )
	{
		/// \todo Decide how to deal with more
		/// than one key - is it more useful to return multiple Data
		/// objects or to put it all in one?
		return NULL;
	}

	switch( array->type )
	{
		case AI_TYPE_BOOLEAN :
			return arrayToDataInternal<bool>( array, AiArrayGetBoolFunc );
		case AI_TYPE_INT :
			return arrayToDataInternal<int>( array, AiArrayGetIntFunc );
		case AI_TYPE_FLOAT :
			return arrayToDataInternal<float>( array, AiArrayGetFltFunc );
		case AI_TYPE_STRING :
			return arrayToDataInternal<string>( array, AiArrayGetStrFunc );
		default :
			return NULL;
	}
}

IECore::DataPtr getParameterInternal( AtNode *node, const char *name, int parameterType )
{
	switch( parameterType )
	{
		case AI_TYPE_BOOLEAN :
			return new BoolData( AiNodeGetBool( node, name ) );
		case AI_TYPE_INT :
			return new IntData( AiNodeGetInt( node, name ) );
		case AI_TYPE_FLOAT :
			return new FloatData( AiNodeGetFlt( node, name ) );
		case AI_TYPE_STRING :
			return new StringData( AiNodeGetStr( node, name ) );
		case AI_TYPE_ARRAY :
			return arrayToData( AiNodeGetArray( node, name ) );
	}
	return NULL;
}

} // namespace

//////////////////////////////////////////////////////////////////////////
// Implementation of public API.
//////////////////////////////////////////////////////////////////////////

namespace IECoreArnold
{

namespace ParameterAlgo
{

void setParameter( AtNode *node, const AtParamEntry *parameter, const IECore::Data *value )
{
	bool isArray = false;
	int type = AiParamGetType( parameter );
	if( type == AI_TYPE_ARRAY )
	{
		type = AiParamGetDefault( parameter )->ARRAY->type;
		isArray = true;
	}

	setParameterInternal( node, AiParamGetName( parameter ), type, isArray, value );
}

void setParameter( AtNode *node, const char *name, const IECore::Data *value )
{
	const AtParamEntry *parameter = AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), name );
	if( parameter )
	{
		setParameter( node, parameter, value );
	}
	else
	{
		bool array = false;
		int type = parameterType( value->typeId(), array );
		if( type != AI_TYPE_NONE )
		{
			std::string typeString = "constant ";
			if( array )
			{
				typeString += "ARRAY ";
			}
			typeString += AiParamGetTypeName( type );
			AiNodeDeclare( node, name, typeString.c_str() );
			setParameterInternal( node, name, type, array, value );
		}
		else
		{
			msg(
				Msg::Warning,
				"setParameter",
				boost::format( "Unsupported data type \"%s\" for name \"%s\"" ) % value->typeName() % name
			);
		}
	}
}

void setParameters( AtNode *node, const IECore::CompoundDataMap &values )
{
	for( CompoundDataMap::const_iterator it=values.begin(); it!=values.end(); it++ )
	{
		setParameter( node, it->first.value().c_str(), it->second.get() );
	}
}

IECore::DataPtr getParameter( AtNode *node, const AtParamEntry *parameter )
{
	return getParameterInternal( node, AiParamGetName( parameter ), AiParamGetType( parameter ) );
}

IECore::DataPtr getParameter( AtNode *node, const AtUserParamEntry *parameter )
{
	return getParameterInternal( node, AiUserParamGetName( parameter ), AiUserParamGetType( parameter ) );
}

IECore::DataPtr getParameter( AtNode *node, const char *name )
{
	const AtParamEntry *parameter = AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), name );
	if( parameter )
	{
		return getParameter( node, parameter );
	}
	else
	{
		const AtUserParamEntry *userParameter = AiNodeLookUpUserParameter( node, name );
		if( userParameter )
		{
			return getParameter( node, userParameter );
		}
	}

	return NULL;
}

void getParameters( AtNode *node, IECore::CompoundDataMap &values )
{
	/// \todo Non-user parameters

	AtUserParamIterator *it = AiNodeGetUserParamIterator( node );
	while( const AtUserParamEntry *param = AiUserParamIteratorGetNext( it ) )
	{
		DataPtr d = getParameter( node, param );
		if( d )
		{
			values[AiUserParamGetName( param )] = d;
		}
		else
		{
			msg(
				Msg::Warning,
				"getParameters",
				boost::format( "Unable to convert user parameter \"%s\"" ) % AiUserParamGetName( param )
			);
		}
	}
	AiUserParamIteratorDestroy( it );
}

int parameterType( IECore::TypeId dataType, bool &array )
{
	switch( dataType )
	{
		// non-array types

		case IntDataTypeId :
			array = false;
			return AI_TYPE_INT;
		case FloatDataTypeId :
			array = false;
			return AI_TYPE_FLOAT;
		case StringDataTypeId :
			array = false;
			return AI_TYPE_STRING;
		case Color3fDataTypeId :
			array = false;
			return AI_TYPE_RGB;
		case BoolDataTypeId :
			array = false;
			return AI_TYPE_BOOLEAN;
		case M44fDataTypeId :
			array = false;
			return AI_TYPE_MATRIX;

		// array types

		case IntVectorDataTypeId :
			array = true;
			return AI_TYPE_INT;
		case FloatVectorDataTypeId :
			array = true;
			return AI_TYPE_FLOAT;
		case StringVectorDataTypeId :
			array = true;
			return AI_TYPE_STRING;
		case Color3fVectorDataTypeId :
			array = true;
			return AI_TYPE_RGB;
		case BoolVectorDataTypeId :
			array = true;
			return AI_TYPE_BOOLEAN;
		case V3fVectorDataTypeId :
			array = true;
			return AI_TYPE_VECTOR;
		case M44fVectorDataTypeId :
			array = true;
			return AI_TYPE_MATRIX;
		default :
			return AI_TYPE_NONE;
	}
}

AtArray *dataToArray( const IECore::Data *data, int aiType )
{
	if( aiType == AI_TYPE_NONE )
	{
		bool isArray = false;
		aiType = parameterType( data->typeId(), isArray );
		if( aiType == AI_TYPE_NONE || !isArray )
		{
			return NULL;
		}
	}

	const void *dataAddress = despatchTypedData<TypedDataAddress, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( data ) );
	size_t dataSize = despatchTypedData<TypedDataSize, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( data ) );
	return AiArrayConvert( dataSize, 1, aiType, dataAddress );
}

IECOREARNOLD_API AtArray *dataToArray( const std::vector<const IECore::Data *> &samples, int aiType )
{
	if( aiType == AI_TYPE_NONE )
	{
		bool isArray = false;
		aiType = parameterType( samples.front()->typeId(), isArray );
		if( aiType == AI_TYPE_NONE || !isArray )
		{
			return NULL;
		}
	}

	size_t arraySize = despatchTypedData<TypedDataSize, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( samples.front() ) );
	ArrayPtr array(
		AiArrayAllocate( arraySize, samples.size(), aiType ),
		AiArrayDestroy
	);

	for( vector<const IECore::Data *>::const_iterator it = samples.begin(), eIt = samples.end(); it != eIt; ++it )
	{
		if( (*it)->typeId() != samples.front()->typeId() )
		{
			throw IECore::Exception( "ParameterAlgo::dataToArray() : Mismatched sample types." );
		}
		const size_t dataSize = despatchTypedData<TypedDataSize, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( *it ) );
		if( dataSize != arraySize )
		{
			throw IECore::Exception( "ParameterAlgo::dataToArray() : Mismatched sample lengths." );
		}
		const void *dataAddress = despatchTypedData<TypedDataAddress, TypeTraits::IsTypedData, DespatchTypedDataIgnoreError>( const_cast<Data *>( *it ) );
		AiArraySetKey( array.get(), /* key = */ it - samples.begin(), dataAddress );
	}

	return array.release();
}

} // namespace ParameterAlgo

} // namespace IECoreArnold
<|endoftext|>


<commit_before>#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <utility>

#include <Python.h>

#include "string.hh"
#include "py.hh"
#include "text.hh"
#include "base.hh"

using namespace py;

//------------------------------------------------------------------------------

namespace {

class String
  : public ExtensionType
{
  std::unique_ptr<fixfmt::String> fmt_;

  static int tp_init(String* self, PyObject* args, PyObject* kw_args)
  {
    static char const* arg_names[]
        = {"size", "ellipsis", "pad", "position", "pad_left", nullptr};

    int         size;
    char const* ellipsis = "...";
    char        pad = ' ';
    double      position = 1.0;
    bool        pad_left = false;
    if (PyArg_ParseTupleAndKeywords(
        args, kw_args, "i|sCdb", (char**) arg_names,
        &size, &ellipsis, &pad, &position, &pad_left)) {
      // FIXME: Validate args.
      self->fmt_ = std::unique_ptr<fixfmt::String>(
          new fixfmt::String(size, ellipsis, pad, position, pad_left));
      return 0;
    }
    else
      return -1;
  }

  static PyObject* tp_call(String* self, PyObject* args, PyObject* kw_args)
  {
    static char const* arg_names[] = {"str", nullptr};
    char* val;
    if (PyArg_ParseTupleAndKeywords(
        args, kw_args, "s", (char**) arg_names, &val))
      return Unicode::from((*self->fmt_)(val)).release();
    else
      return nullptr;
  }

  static PyMethodDef const tp_methods[];

public:

  static PyTypeObject type;

};

PyMethodDef const String::tp_methods[] = {
  METHODDEF_END
};

PyTypeObject String::type = {
  PyVarObject_HEAD_INIT(nullptr, 0)
  "fixfmt.String",             // tp_name
  sizeof(String),              // tp_basicsize
  0,                           // tp_itemsize
  nullptr,                     // tp_dealloc
  nullptr,                     // tp_print
  nullptr,                     // tp_getattr
  nullptr,                     // tp_setattr
  nullptr,                     // tp_reserved
  nullptr,                     // tp_repr
  nullptr,                     // tp_as_number
  nullptr,                     // tp_as_sequence
  nullptr,                     // tp_as_mapping
  nullptr,                     // tp_hash
  (ternaryfunc) tp_call,       // tp_call
  nullptr,                     // tp_str
  nullptr,                     // tp_getattro
  nullptr,                     // tp_setattro
  nullptr,                     // tp_as_buffer
  Py_TPFLAGS_DEFAULT
  | Py_TPFLAGS_BASETYPE,       // tp_flags
  nullptr,                     // tp_doc
  nullptr,                     // tp_traverse
  nullptr,                     // tp_clear
  nullptr,                     // tp_richcompare
  0,                           // tp_weaklistoffset
  nullptr,                     // tp_iter
  nullptr,                     // tp_iternext
  (PyMethodDef*) tp_methods,   // tp_methods
  nullptr,                     // tp_members
  nullptr,                     // tp_getset
  nullptr,                     // tp_base
  nullptr,                     // tp_dict
  nullptr,                     // tp_descr_get
  nullptr,                     // tp_descr_set
  0,                           // tp_dictoffset
  (initproc) tp_init,          // tp_init
  nullptr,                     // tp_alloc
  PyType_GenericNew,           // tp_new
};

}  // anonymous namespace

PyTypeObject* get_String()
{
  int result = PyType_Ready(&String::type);
  assert(result == 0); unused(result);
  return &String::type;
}

<commit_msg>Order imports.<commit_after>#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <utility>

#include <Python.h>

#include "base.hh"
#include "py.hh"
#include "string.hh"
#include "text.hh"

using namespace py;

//------------------------------------------------------------------------------

namespace {

class String
  : public ExtensionType
{
  std::unique_ptr<fixfmt::String> fmt_;

  static int tp_init(String* self, PyObject* args, PyObject* kw_args)
  {
    static char const* arg_names[]
        = {"size", "ellipsis", "pad", "position", "pad_left", nullptr};

    int         size;
    char const* ellipsis = "...";
    char        pad = ' ';
    double      position = 1.0;
    bool        pad_left = false;
    if (PyArg_ParseTupleAndKeywords(
        args, kw_args, "i|sCdb", (char**) arg_names,
        &size, &ellipsis, &pad, &position, &pad_left)) {
      // FIXME: Validate args.
      self->fmt_ = std::unique_ptr<fixfmt::String>(
          new fixfmt::String(size, ellipsis, pad, position, pad_left));
      return 0;
    }
    else
      return -1;
  }

  static PyObject* tp_call(String* self, PyObject* args, PyObject* kw_args)
  {
    static char const* arg_names[] = {"str", nullptr};
    char* val;
    if (PyArg_ParseTupleAndKeywords(
        args, kw_args, "s", (char**) arg_names, &val))
      return Unicode::from((*self->fmt_)(val)).release();
    else
      return nullptr;
  }

  static PyMethodDef const tp_methods[];

public:

  static PyTypeObject type;

};

PyMethodDef const String::tp_methods[] = {
  METHODDEF_END
};

PyTypeObject String::type = {
  PyVarObject_HEAD_INIT(nullptr, 0)
  "fixfmt.String",             // tp_name
  sizeof(String),              // tp_basicsize
  0,                           // tp_itemsize
  nullptr,                     // tp_dealloc
  nullptr,                     // tp_print
  nullptr,                     // tp_getattr
  nullptr,                     // tp_setattr
  nullptr,                     // tp_reserved
  nullptr,                     // tp_repr
  nullptr,                     // tp_as_number
  nullptr,                     // tp_as_sequence
  nullptr,                     // tp_as_mapping
  nullptr,                     // tp_hash
  (ternaryfunc) tp_call,       // tp_call
  nullptr,                     // tp_str
  nullptr,                     // tp_getattro
  nullptr,                     // tp_setattro
  nullptr,                     // tp_as_buffer
  Py_TPFLAGS_DEFAULT
  | Py_TPFLAGS_BASETYPE,       // tp_flags
  nullptr,                     // tp_doc
  nullptr,                     // tp_traverse
  nullptr,                     // tp_clear
  nullptr,                     // tp_richcompare
  0,                           // tp_weaklistoffset
  nullptr,                     // tp_iter
  nullptr,                     // tp_iternext
  (PyMethodDef*) tp_methods,   // tp_methods
  nullptr,                     // tp_members
  nullptr,                     // tp_getset
  nullptr,                     // tp_base
  nullptr,                     // tp_dict
  nullptr,                     // tp_descr_get
  nullptr,                     // tp_descr_set
  0,                           // tp_dictoffset
  (initproc) tp_init,          // tp_init
  nullptr,                     // tp_alloc
  PyType_GenericNew,           // tp_new
};

}  // anonymous namespace

PyTypeObject* get_String()
{
  int result = PyType_Ready(&String::type);
  assert(result == 0); unused(result);
  return &String::type;
}

<|endoftext|>


<commit_before>#include "ResourceView.hpp"

#include <Engine/Geometry/Model.hpp>
#include <Engine/Texture/TextureAsset.hpp>
#include <Engine/Audio/SoundBuffer.hpp>
#include <Engine/Script/ScriptFile.hpp>
#include <Engine/Util/FileSystem.hpp>
#include <Engine/Hymn.hpp>
#include <DefaultAlbedo.png.hpp>
#include <Engine/MainWindow.hpp>
#include <imgui.h>
#include <limits>
#include "../ImGui/Splitter.hpp"
#include <Engine/Manager/Managers.hpp>
#include <Engine/Manager/ResourceManager.hpp>
#include <cstdio>
#include <Utility/Log.hpp>

using namespace GUI;
using namespace std;

ResourceView::ResourceView() {
    folderNameWindow.SetClosedCallback(std::bind(&ResourceView::FileNameWindowClosed, this, placeholders::_1));
}

void ResourceView::Show() {
    ImVec2 size(MainWindow::GetInstance()->GetSize().x, MainWindow::GetInstance()->GetSize().y);

    // Splitter.
    ImGui::VerticalSplitter(ImVec2(sceneWidth, size.y - resourceHeight), size.x - sceneWidth - editorWidth, splitterSize, resourceHeight, resourceResize, 20, size.y - 20);
    if (resourceResize)
        resourceHeight = size.y - resourceHeight;

    ImGui::SetNextWindowPos(ImVec2(sceneWidth, size.y - resourceHeight));
    ImGui::SetNextWindowSize(ImVec2(size.x - sceneWidth - editorWidth, resourceHeight));

    ImGui::Begin("Resources", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_ShowBorders);

    // Show resources.
    scriptPressed = false;
    texturePressed = false;
    modelPressed = false;
    soundPressed = false;

    ShowResourceFolder(Resources().resourceFolder, Resources().resourceFolder.name);

    // Change scene.
    if (changeScene) {
        if (Hymn().GetPath() != "") {
            savePromptWindow.SetVisible(true);
            savePromptWindow.Show();

            switch (savePromptWindow.GetDecision()) {
            case 0:
                sceneEditor.Save();
                sceneEditor.SetVisible(true);
                sceneEditor.SetScene(resourcePath, scene);
                Resources().activeScene = resourcePath + "/" + *scene;
                sceneEditor.entityEditor.SetVisible(false);
                Hymn().world.Clear();
                Hymn().world.Load(Hymn().GetPath() + "/" + Resources().activeScene + ".json");
                changeScene = false;
                savePromptWindow.SetVisible(false);
                savePromptWindow.ResetDecision();
                break;

            case 1:
                sceneEditor.SetVisible(true);
                sceneEditor.SetScene(resourcePath, scene);
                Resources().activeScene = resourcePath + "/" + *scene;
                sceneEditor.entityEditor.SetVisible(false);
                Hymn().world.Clear();
                Hymn().world.Load(Hymn().GetPath() + "/" + Resources().activeScene + ".json");
                changeScene = false;
                savePromptWindow.SetVisible(false);
                savePromptWindow.ResetDecision();
                break;

            case 2:
                changeScene = false;
                savePromptWindow.ResetDecision();
                savePromptWindow.SetVisible(false);
                break;

            default:
                break;
            }
        }
    }

    // Create folder.
    if (folderNameWindow.IsVisible())
        folderNameWindow.Show();

    if (sceneEditor.entityPressed || scriptPressed || texturePressed || modelPressed || soundPressed) {
        sceneEditor.entityEditor.SetVisible(sceneEditor.entityPressed);
        scriptEditor.SetVisible(scriptPressed);
        textureEditor.SetVisible(texturePressed);
        modelEditor.SetVisible(modelPressed);
        soundEditor.SetVisible(soundPressed);
    }

    if (sceneEditor.IsVisible()) {
        ImGui::HorizontalSplitter(ImVec2(sceneWidth, 20), size.y - 20, splitterSize, sceneWidth, sceneResize, 20, size.x - editorWidth - 20);
        ImGui::SetNextWindowPos(ImVec2(0, 20));
        ImGui::SetNextWindowSize(ImVec2(sceneWidth, size.y - 20));
        sceneEditor.Show();
    }

    if (sceneEditor.entityEditor.IsVisible() || scriptEditor.IsVisible() || textureEditor.IsVisible() || modelEditor.IsVisible() || soundEditor.IsVisible()) {
        editorWidth = size.x - editorWidth;
        ImGui::HorizontalSplitter(ImVec2(editorWidth, 20), size.y - 20, splitterSize, editorWidth, editorResize, sceneWidth + 20, size.x - 20);
        editorWidth = size.x - editorWidth;

        ImGui::SetNextWindowPos(ImVec2(size.x - editorWidth, 20));
        ImGui::SetNextWindowSize(ImVec2(editorWidth, size.y - 20));
    }

    if (sceneEditor.entityEditor.IsVisible())
        sceneEditor.entityEditor.Show();
    if (scriptEditor.IsVisible())
        scriptEditor.Show();
    if (textureEditor.IsVisible())
        textureEditor.Show();
    if (modelEditor.IsVisible())
        modelEditor.Show();
    if (soundEditor.IsVisible())
        soundEditor.Show();

    ImGui::End();
}

bool ResourceView::IsVisible() const {
    return visible;
}

void ResourceView::SetVisible(bool visible) {
    this->visible = visible;
}

void ResourceView::HideEditors() {
    sceneEditor.SetVisible(false);
    sceneEditor.entityEditor.SetVisible(false);
    scriptEditor.SetVisible(false);
    modelEditor.SetVisible(false);
    textureEditor.SetVisible(false);
    soundEditor.SetVisible(false);
}

void ResourceView::SaveScene() const {
    sceneEditor.Save();
}

#undef max
void ResourceView::ResetScene() {
    sceneEditor.SetScene("", nullptr);
    sceneEditor.SetVisible(false);
}

SceneEditor& ResourceView::GetScene() {
    return sceneEditor;
}

void ResourceView::ShowResourceFolder(ResourceList::ResourceFolder& folder, const std::string& path) {
    bool opened = ImGui::TreeNode(folder.name.c_str());

    if (ImGui::BeginPopupContextItem(folder.name.c_str())) {
        // Add subfolder.
        if (ImGui::Selectable("Add folder")) {
            resourcePath = path;
            parentFolder = &folder;
            folderNameWindow.SetVisible(true);
            return;
        }

        // Add scene.
        if (ImGui::Selectable("Add scene")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::SCENE;
            resource.scene = "Scene #" + std::to_string(Resources().sceneNumber++);
            folder.resources.push_back(resource);
            return;
        }

        // Add model.
        if (ImGui::Selectable("Add model")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::MODEL;
            resource.model = new Geometry::Model();
            resource.model->path = path + "/";
            resource.model->name = "Model #" + std::to_string(Resources().modelNumber++);
            folder.resources.push_back(resource);
            return;
        }

        // Add texture.
        if (ImGui::Selectable("Add texture")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::TEXTURE;
            string name = path + "/Texture #" + std::to_string(Resources().textureNumber++);
            resource.texture = Managers().resourceManager->CreateTextureAsset(name, Managers().resourceManager->CreateTexture2D(DEFAULTALBEDO_PNG, DEFAULTALBEDO_PNG_LENGTH));
            folder.resources.push_back(resource);
            return;
        }

        // Add script.
        if (ImGui::Selectable("Add script")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::SCRIPT;
            resource.script = new ScriptFile();
            resource.script->path = path + "/";
            resource.script->name = "Script #" + std::to_string(Hymn().scriptNumber++);
            Hymn().scripts.push_back(resource.script);
            folder.resources.push_back(resource);
            return;
        }

        // Add sound.
        if (ImGui::Selectable("Add sound")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::SOUND;
            resource.sound = new Audio::SoundBuffer();
            resource.sound->path = path + "/";
            resource.sound->name = "Sound #" + std::to_string(Resources().soundNumber++);
            folder.resources.push_back(resource);
            return;
        }

        /// @todo Remove folder.

        ImGui::EndPopup();
    }

    if (opened) {
        // Show subfolders.
        for (ResourceList::ResourceFolder& subfolder : folder.subfolders) {
            ShowResourceFolder(subfolder, path + "/" + subfolder.name);
        }

        // Show resources.
        for (auto it = folder.resources.begin(); it != folder.resources.end(); ++it) {
            if (ShowResource(*it, path)) {
                folder.resources.erase(it);
                return;
            }
        }

        ImGui::TreePop();
    }
}

bool ResourceView::ShowResource(ResourceList::Resource& resource, const std::string& path) {
    // Scene.
    if (resource.type == ResourceList::Resource::SCENE) {
        if (ImGui::Selectable(resource.scene.c_str())) {
            // Sets to don't save when opening first scene.
            if (scene == nullptr) {
                changeScene = true;
                resourcePath = path;
                scene = &resource.scene;
                savePromptWindow.SetVisible(false);
                savePromptWindow.SetDecision(1);
            } else {
                // Does so that the prompt window won't show if you select active scene.
                if (resource.scene != Resources().activeScene) {
                    changeScene = true;
                    resourcePath = path;
                    scene = &resource.scene;
                    savePromptWindow.SetTitle("Save before you switch scene?");
                }
            }
        }

        // Delete scene.
        if (ImGui::BeginPopupContextItem(resource.scene.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (Resources().activeScene == resource.scene) {
                    Resources().activeScene = "";
                    sceneEditor.SetScene("", nullptr);
                }

                ImGui::EndPopup();

                return true;
            }
            ImGui::EndPopup();
        }
    }

    // Model.
    if (resource.type == ResourceList::Resource::MODEL) {
        if (ImGui::Selectable(resource.model->name.c_str())) {
            modelPressed = true;
            modelEditor.SetModel(resource.model);
        }

        if (ImGui::BeginPopupContextItem(resource.model->name.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (modelEditor.GetModel() == resource.model)
                    modelEditor.SetVisible(false);

                Managers().resourceManager->FreeModel(resource.model);
                ImGui::EndPopup();

                return true;
            }
            ImGui::EndPopup();
        }
    }

    // Textures.
    if (resource.type == ResourceList::Resource::TEXTURE) {
        if (ImGui::Selectable(resource.texture->name.c_str())) {
            texturePressed = true;
            textureEditor.SetTexture(resource.texture);
        }

        if (ImGui::BeginPopupContextItem(resource.texture->name.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (Managers().resourceManager->GetTextureAssetInstanceCount(resource.texture) > 1) {
                    Log() << "This texture is in use. Remove all references to the texture first.\n";
                } else {
                    if (textureEditor.GetTexture() == resource.texture)
                        textureEditor.SetVisible(false);

                    // Remove files.
                    remove((Hymn().GetPath() + "/" + path + "/" + resource.texture->name + ".png").c_str());
                    remove((Hymn().GetPath() + "/" + path + "/" + resource.texture->name + ".json").c_str());

                    Managers().resourceManager->FreeTextureAsset(resource.texture);
                    ImGui::EndPopup();
                    return true;
                }
            }
            ImGui::EndPopup();
        }
    }
    /*
    // Scripts.
    bool scriptPressed = false;
    if (ImGui::TreeNode("Scripts")) {
        for (auto it = Hymn().scripts.begin(); it != Hymn().scripts.end(); ++it) {
            ScriptFile* script = *it;
            std::string name = script->name;

            if (ImGui::Selectable(name.c_str())) {
                scriptPressed = true;
                scriptEditor.SetScript(script);
            }

            if (ImGui::BeginPopupContextItem(name.c_str())) {
                if (ImGui::Selectable("Delete")) {
                    if (scriptEditor.GetScript() == script)
                        scriptEditor.SetVisible(false);

                    delete script;
                    Hymn().scripts.erase(it);
                    ImGui::EndPopup();
                    break;
                }
                ImGui::EndPopup();
            }
        }

        ImGui::TreePop();
    }

    // Sounds.
    bool soundPressed = false;
    if (ImGui::TreeNode("Sounds")) {
        for (auto it = Resources().sounds.begin(); it != Resources().sounds.end(); ++it) {
            Audio::SoundBuffer* sound = *it;
            if (ImGui::Selectable(sound->name.c_str())) {
                soundPressed = true;
                soundEditor.SetSound(sound);
            }

            if (ImGui::BeginPopupContextItem(sound->name.c_str())) {
                if (ImGui::Selectable("Delete")) {
                    if (soundEditor.GetSound() == sound)
                        soundEditor.SetVisible(false);

                    delete sound;
                    Resources().sounds.erase(it);
                    ImGui::EndPopup();
                    break;
                }
                ImGui::EndPopup();
            }
        }

        ImGui::TreePop();
    }*/

    return false;
}

void ResourceView::FileNameWindowClosed(const std::string& name) {
    if (!name.empty()) {
        ResourceList::ResourceFolder folder;
        folder.name = name;
        parentFolder->subfolders.push_back(folder);

        FileSystem::CreateDirectory((Hymn().GetPath() + "/" + resourcePath + "/" + name).c_str());
    }
}
<commit_msg>Show scripts<commit_after>#include "ResourceView.hpp"

#include <Engine/Geometry/Model.hpp>
#include <Engine/Texture/TextureAsset.hpp>
#include <Engine/Audio/SoundBuffer.hpp>
#include <Engine/Script/ScriptFile.hpp>
#include <Engine/Util/FileSystem.hpp>
#include <Engine/Hymn.hpp>
#include <DefaultAlbedo.png.hpp>
#include <Engine/MainWindow.hpp>
#include <imgui.h>
#include <limits>
#include "../ImGui/Splitter.hpp"
#include <Engine/Manager/Managers.hpp>
#include <Engine/Manager/ResourceManager.hpp>
#include <cstdio>
#include <Utility/Log.hpp>

using namespace GUI;
using namespace std;

ResourceView::ResourceView() {
    folderNameWindow.SetClosedCallback(std::bind(&ResourceView::FileNameWindowClosed, this, placeholders::_1));
}

void ResourceView::Show() {
    ImVec2 size(MainWindow::GetInstance()->GetSize().x, MainWindow::GetInstance()->GetSize().y);

    // Splitter.
    ImGui::VerticalSplitter(ImVec2(sceneWidth, size.y - resourceHeight), size.x - sceneWidth - editorWidth, splitterSize, resourceHeight, resourceResize, 20, size.y - 20);
    if (resourceResize)
        resourceHeight = size.y - resourceHeight;

    ImGui::SetNextWindowPos(ImVec2(sceneWidth, size.y - resourceHeight));
    ImGui::SetNextWindowSize(ImVec2(size.x - sceneWidth - editorWidth, resourceHeight));

    ImGui::Begin("Resources", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_ShowBorders);

    // Show resources.
    scriptPressed = false;
    texturePressed = false;
    modelPressed = false;
    soundPressed = false;

    ShowResourceFolder(Resources().resourceFolder, Resources().resourceFolder.name);

    // Change scene.
    if (changeScene) {
        if (Hymn().GetPath() != "") {
            savePromptWindow.SetVisible(true);
            savePromptWindow.Show();

            switch (savePromptWindow.GetDecision()) {
            case 0:
                sceneEditor.Save();
                sceneEditor.SetVisible(true);
                sceneEditor.SetScene(resourcePath, scene);
                Resources().activeScene = resourcePath + "/" + *scene;
                sceneEditor.entityEditor.SetVisible(false);
                Hymn().world.Clear();
                Hymn().world.Load(Hymn().GetPath() + "/" + Resources().activeScene + ".json");
                changeScene = false;
                savePromptWindow.SetVisible(false);
                savePromptWindow.ResetDecision();
                break;

            case 1:
                sceneEditor.SetVisible(true);
                sceneEditor.SetScene(resourcePath, scene);
                Resources().activeScene = resourcePath + "/" + *scene;
                sceneEditor.entityEditor.SetVisible(false);
                Hymn().world.Clear();
                Hymn().world.Load(Hymn().GetPath() + "/" + Resources().activeScene + ".json");
                changeScene = false;
                savePromptWindow.SetVisible(false);
                savePromptWindow.ResetDecision();
                break;

            case 2:
                changeScene = false;
                savePromptWindow.ResetDecision();
                savePromptWindow.SetVisible(false);
                break;

            default:
                break;
            }
        }
    }

    // Create folder.
    if (folderNameWindow.IsVisible())
        folderNameWindow.Show();

    if (sceneEditor.entityPressed || scriptPressed || texturePressed || modelPressed || soundPressed) {
        sceneEditor.entityEditor.SetVisible(sceneEditor.entityPressed);
        scriptEditor.SetVisible(scriptPressed);
        textureEditor.SetVisible(texturePressed);
        modelEditor.SetVisible(modelPressed);
        soundEditor.SetVisible(soundPressed);
    }

    if (sceneEditor.IsVisible()) {
        ImGui::HorizontalSplitter(ImVec2(sceneWidth, 20), size.y - 20, splitterSize, sceneWidth, sceneResize, 20, size.x - editorWidth - 20);
        ImGui::SetNextWindowPos(ImVec2(0, 20));
        ImGui::SetNextWindowSize(ImVec2(sceneWidth, size.y - 20));
        sceneEditor.Show();
    }

    if (sceneEditor.entityEditor.IsVisible() || scriptEditor.IsVisible() || textureEditor.IsVisible() || modelEditor.IsVisible() || soundEditor.IsVisible()) {
        editorWidth = size.x - editorWidth;
        ImGui::HorizontalSplitter(ImVec2(editorWidth, 20), size.y - 20, splitterSize, editorWidth, editorResize, sceneWidth + 20, size.x - 20);
        editorWidth = size.x - editorWidth;

        ImGui::SetNextWindowPos(ImVec2(size.x - editorWidth, 20));
        ImGui::SetNextWindowSize(ImVec2(editorWidth, size.y - 20));
    }

    if (sceneEditor.entityEditor.IsVisible())
        sceneEditor.entityEditor.Show();
    if (scriptEditor.IsVisible())
        scriptEditor.Show();
    if (textureEditor.IsVisible())
        textureEditor.Show();
    if (modelEditor.IsVisible())
        modelEditor.Show();
    if (soundEditor.IsVisible())
        soundEditor.Show();

    ImGui::End();
}

bool ResourceView::IsVisible() const {
    return visible;
}

void ResourceView::SetVisible(bool visible) {
    this->visible = visible;
}

void ResourceView::HideEditors() {
    sceneEditor.SetVisible(false);
    sceneEditor.entityEditor.SetVisible(false);
    scriptEditor.SetVisible(false);
    modelEditor.SetVisible(false);
    textureEditor.SetVisible(false);
    soundEditor.SetVisible(false);
}

void ResourceView::SaveScene() const {
    sceneEditor.Save();
}

#undef max
void ResourceView::ResetScene() {
    sceneEditor.SetScene("", nullptr);
    sceneEditor.SetVisible(false);
}

SceneEditor& ResourceView::GetScene() {
    return sceneEditor;
}

void ResourceView::ShowResourceFolder(ResourceList::ResourceFolder& folder, const std::string& path) {
    bool opened = ImGui::TreeNode(folder.name.c_str());

    if (ImGui::BeginPopupContextItem(folder.name.c_str())) {
        // Add subfolder.
        if (ImGui::Selectable("Add folder")) {
            resourcePath = path;
            parentFolder = &folder;
            folderNameWindow.SetVisible(true);
            return;
        }

        // Add scene.
        if (ImGui::Selectable("Add scene")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::SCENE;
            resource.scene = "Scene #" + std::to_string(Resources().sceneNumber++);
            folder.resources.push_back(resource);
            return;
        }

        // Add model.
        if (ImGui::Selectable("Add model")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::MODEL;
            resource.model = new Geometry::Model();
            resource.model->path = path + "/";
            resource.model->name = "Model #" + std::to_string(Resources().modelNumber++);
            folder.resources.push_back(resource);
            return;
        }

        // Add texture.
        if (ImGui::Selectable("Add texture")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::TEXTURE;
            string name = path + "/Texture #" + std::to_string(Resources().textureNumber++);
            resource.texture = Managers().resourceManager->CreateTextureAsset(name, Managers().resourceManager->CreateTexture2D(DEFAULTALBEDO_PNG, DEFAULTALBEDO_PNG_LENGTH));
            folder.resources.push_back(resource);
            return;
        }

        // Add script.
        if (ImGui::Selectable("Add script")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::SCRIPT;
            resource.script = new ScriptFile();
            resource.script->path = path + "/";
            resource.script->name = "Script #" + std::to_string(Hymn().scriptNumber++);
            Hymn().scripts.push_back(resource.script);
            folder.resources.push_back(resource);
            return;
        }

        // Add sound.
        if (ImGui::Selectable("Add sound")) {
            ResourceList::Resource resource;
            resource.type = ResourceList::Resource::SOUND;
            resource.sound = new Audio::SoundBuffer();
            resource.sound->path = path + "/";
            resource.sound->name = "Sound #" + std::to_string(Resources().soundNumber++);
            folder.resources.push_back(resource);
            return;
        }

        /// @todo Remove folder.

        ImGui::EndPopup();
    }

    if (opened) {
        // Show subfolders.
        for (ResourceList::ResourceFolder& subfolder : folder.subfolders) {
            ShowResourceFolder(subfolder, path + "/" + subfolder.name);
        }

        // Show resources.
        for (auto it = folder.resources.begin(); it != folder.resources.end(); ++it) {
            if (ShowResource(*it, path)) {
                folder.resources.erase(it);
                return;
            }
        }

        ImGui::TreePop();
    }
}

bool ResourceView::ShowResource(ResourceList::Resource& resource, const std::string& path) {
    // Scene.
    if (resource.type == ResourceList::Resource::SCENE) {
        if (ImGui::Selectable(resource.scene.c_str())) {
            // Sets to don't save when opening first scene.
            if (scene == nullptr) {
                changeScene = true;
                resourcePath = path;
                scene = &resource.scene;
                savePromptWindow.SetVisible(false);
                savePromptWindow.SetDecision(1);
            } else {
                // Does so that the prompt window won't show if you select active scene.
                if (resource.scene != Resources().activeScene) {
                    changeScene = true;
                    resourcePath = path;
                    scene = &resource.scene;
                    savePromptWindow.SetTitle("Save before you switch scene?");
                }
            }
        }

        // Delete scene.
        if (ImGui::BeginPopupContextItem(resource.scene.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (Resources().activeScene == resource.scene) {
                    Resources().activeScene = "";
                    sceneEditor.SetScene("", nullptr);
                }

                ImGui::EndPopup();

                return true;
            }
            ImGui::EndPopup();
        }
    }

    // Model.
    if (resource.type == ResourceList::Resource::MODEL) {
        if (ImGui::Selectable(resource.model->name.c_str())) {
            modelPressed = true;
            modelEditor.SetModel(resource.model);
        }

        if (ImGui::BeginPopupContextItem(resource.model->name.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (modelEditor.GetModel() == resource.model)
                    modelEditor.SetVisible(false);

                Managers().resourceManager->FreeModel(resource.model);
                ImGui::EndPopup();

                return true;
            }
            ImGui::EndPopup();
        }
    }

    // Textures.
    if (resource.type == ResourceList::Resource::TEXTURE) {
        if (ImGui::Selectable(resource.texture->name.c_str())) {
            texturePressed = true;
            textureEditor.SetTexture(resource.texture);
        }

        if (ImGui::BeginPopupContextItem(resource.texture->name.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (Managers().resourceManager->GetTextureAssetInstanceCount(resource.texture) > 1) {
                    Log() << "This texture is in use. Remove all references to the texture first.\n";
                } else {
                    if (textureEditor.GetTexture() == resource.texture)
                        textureEditor.SetVisible(false);

                    // Remove files.
                    remove((Hymn().GetPath() + "/" + path + "/" + resource.texture->name + ".png").c_str());
                    remove((Hymn().GetPath() + "/" + path + "/" + resource.texture->name + ".json").c_str());

                    Managers().resourceManager->FreeTextureAsset(resource.texture);
                    ImGui::EndPopup();
                    return true;
                }
            }
            ImGui::EndPopup();
        }
    }

    // Scripts.
    if (resource.type == ResourceList::Resource::SCRIPT) {
        std::string name = resource.script->name;

        if (ImGui::Selectable(name.c_str())) {
            scriptPressed = true;
            scriptEditor.SetScript(resource.script);
        }

        if (ImGui::BeginPopupContextItem(name.c_str())) {
            if (ImGui::Selectable("Delete")) {
                if (scriptEditor.GetScript() == resource.script)
                    scriptEditor.SetVisible(false);

                Managers().resourceManager->FreeScriptFile(resource.script);
                for (auto it = Hymn().scripts.begin(); it != Hymn().scripts.end(); ++it) {
                    if (*it == resource.script) {
                        Hymn().scripts.erase(it);
                        break;
                    }
                }
                ImGui::EndPopup();
                return true;
            }
            ImGui::EndPopup();
        }
    }
    /*
    // Sounds.
    bool soundPressed = false;
    if (ImGui::TreeNode("Sounds")) {
        for (auto it = Resources().sounds.begin(); it != Resources().sounds.end(); ++it) {
            Audio::SoundBuffer* sound = *it;
            if (ImGui::Selectable(sound->name.c_str())) {
                soundPressed = true;
                soundEditor.SetSound(sound);
            }

            if (ImGui::BeginPopupContextItem(sound->name.c_str())) {
                if (ImGui::Selectable("Delete")) {
                    if (soundEditor.GetSound() == sound)
                        soundEditor.SetVisible(false);

                    delete sound;
                    Resources().sounds.erase(it);
                    ImGui::EndPopup();
                    break;
                }
                ImGui::EndPopup();
            }
        }

        ImGui::TreePop();
    }*/

    return false;
}

void ResourceView::FileNameWindowClosed(const std::string& name) {
    if (!name.empty()) {
        ResourceList::ResourceFolder folder;
        folder.name = name;
        parentFolder->subfolders.push_back(folder);

        FileSystem::CreateDirectory((Hymn().GetPath() + "/" + resourcePath + "/" + name).c_str());
    }
}
<|endoftext|>


<commit_before>/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/

#include "scene_io.h"
#include "image_io.h"
#include "SceneGraph/scene1.h"
#include "SceneGraph/shape.h"
#include "SceneGraph/material.h"
#include "SceneGraph/light.h"
#include "SceneGraph/texture.h"
#include "math/mathutils.h"

#include "SceneGraph/uberv2material.h"
#include "SceneGraph/inputmaps.h"

#include <string>
#include <map>
#include <set>
#include <cassert>

#include "Utils/tiny_obj_loader.h"
#include "Utils/log.h"

namespace Baikal
{
    // Obj scene loader
    class SceneIoObj : public SceneIo::Loader
    {
    public:
        // Load scene from file
        Scene1::Ptr LoadScene(std::string const& filename, std::string const& basepath) const override;
        SceneIoObj() : SceneIo::Loader("obj", this)
        {
            SceneIo::RegisterLoader("objm", this);
        }
        ~SceneIoObj()
        {
            SceneIo::UnregisterLoader("objm");
        }

    private:
        Material::Ptr TranslateMaterialUberV2(ImageIo const& image_io, tinyobj::material_t const& mat, std::string const& basepath, Scene1& scene) const;

        mutable std::map<std::string, Material::Ptr> m_material_cache;
    };

    // Create static object to register loader. This object will be used as loader
    static SceneIoObj obj_loader;

    Scene1::Ptr SceneIoObj::LoadScene(std::string const& filename, std::string const& basepath) const
    {
        using namespace tinyobj;

        auto image_io(ImageIo::CreateImageIo());

        // Loader data
        std::vector<shape_t> objshapes;
        std::vector<material_t> objmaterials;

        // Try loading file
        LogInfo("Loading a scene from OBJ: ", filename, " ... ");
        std::string err;
        auto res = LoadObj(objshapes, objmaterials, err, filename.c_str(), basepath.c_str(), triangulation|calculate_normals);
        if (!res)
        {
            throw std::runtime_error(err);
        }
        LogInfo("Success\n");

        // Allocate scene
        auto scene = Scene1::Create();

        // Enumerate and translate materials
        // Keep track of emissive subset
        std::set<Material::Ptr> emissives;
        std::vector<Material::Ptr> materials(objmaterials.size());
        for (int i = 0; i < (int)objmaterials.size(); ++i)
        {
            // Translate material
            materials[i] = TranslateMaterialUberV2(*image_io, objmaterials[i], basepath, *scene);

            // Add to emissive subset if needed
            if (materials[i]->HasEmission())
            {
                emissives.insert(materials[i]);
            }
        }

        // Enumerate all shapes in the scene
        for (int s = 0; s < (int)objshapes.size(); ++s)
        {
            const auto& shape = objshapes[s];

            // Find all materials used by this shape.
            std::set<int> used_materials(std::begin(shape.mesh.material_ids), std::end(shape.mesh.material_ids));

            // Split the mesh into multiple meshes, each with only one material.
            for (int used_material : used_materials)
            {
                // Map from old index to new index.
                std::map<unsigned int, unsigned int> used_indices;

                // Remapped indices.
                std::vector<unsigned int> indices;

                // Collected vertex/normal/texcoord data.
                std::vector<float> vertices, normals, texcoords;

                // Go through each face in the mesh.
                for (size_t i = 0; i < shape.mesh.material_ids.size(); ++i)
                {
                    // Skip faces which don't use the current material.
                    if (shape.mesh.material_ids[i] != used_material) continue;

                    const int num_face_vertices = shape.mesh.num_vertices[i];
                    assert(num_face_vertices == 3 && "expected triangles");
                    // For each vertex index of this face.
                    for (int j = 0; j < num_face_vertices; ++j)
                    {
                        const unsigned int old_index = shape.mesh.indices[num_face_vertices * i + j];
                        // Collect vertex/normal/texcoord data. Avoid inserting the same data twice.
                        auto result = used_indices.emplace(old_index, (unsigned int)(vertices.size() / 3));
                        if (result.second) // Did insert?
                        {
                            // Push the new data.
                            for (int k = 0; k < 3; ++k)
                                vertices.push_back(shape.mesh.positions[3 * old_index + k]);
                            for (int k = 0; k < 3; ++k)
                                normals.push_back(shape.mesh.normals[3 * old_index + k]);
                            if (!shape.mesh.texcoords.empty())
                                for (int k = 0; k < 2; ++k)
                                    texcoords.push_back(shape.mesh.texcoords[2 * old_index + k]);
                        }
                        const unsigned int new_index = result.first->second;
                        indices.push_back(new_index);
                    }
                }

                // Create empty mesh
                auto mesh = Mesh::Create();

                // Set vertex and index data
                auto num_vertices = vertices.size() / 3;
                mesh->SetVertices(&vertices[0], num_vertices);

                auto num_normals = normals.size() / 3;
                mesh->SetNormals(&normals[0], num_normals);

                auto num_uvs = texcoords.size() / 2;

                // If we do not have UVs, generate zeroes
                if (num_uvs)
                {
                    mesh->SetUVs(&texcoords[0], num_uvs);
                }
                else
                {
                    std::vector<RadeonRays::float2> zero(num_vertices);
                    std::fill(zero.begin(), zero.end(), RadeonRays::float2(0, 0));
                    mesh->SetUVs(&zero[0], num_vertices);
                }

                // Set indices
                auto num_indices = indices.size();
                mesh->SetIndices(reinterpret_cast<std::uint32_t const*>(&indices[0]), num_indices);

                // Set material

                if (used_material >= 0)
                {
                    mesh->SetMaterial(materials[used_material]);
                }

                // Attach to the scene
                scene->AttachShape(mesh);

                // If the mesh has emissive material we need to add area light for it
                if (used_material >= 0 && emissives.find(materials[used_material]) != emissives.cend())
                {
                    // Add area light for each polygon of emissive mesh
                    for (std::size_t l = 0; l < mesh->GetNumIndices() / 3; ++l)
                    {
                        auto light = AreaLight::Create(mesh, l);
                        scene->AttachLight(light);
                    }
                }
            }
        }

        // TODO: temporary code, add IBL
        auto ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr");
        //auto ibl_texture1 = image_io->LoadImage("../Resources/Textures/sky.hdr");

        auto ibl = ImageBasedLight::Create();
        ibl->SetTexture(ibl_texture);
        //ibl->SetReflectionTexture(ibl_texture1);
        //ibl->SetBackgroundTexture(ibl_texture);
        ibl->SetMultiplier(1.f);

        // TODO: temporary code to add directional light
        auto light = DirectionalLight::Create();
        light->SetDirection(RadeonRays::float3(.1f, -1.f, -.1f));
        light->SetEmittedRadiance(RadeonRays::float3(1.f, 1.f, 1.f));

        /*auto light1 = DirectionalLight::Create();
        auto d = RadeonRays::float3(-0.1f, -1.f, -1.f);
        d.normalize();
        light1->SetDirection(d);
        light1->SetEmittedRadiance(3.f * RadeonRays::float3(1.f, 0.8f, 0.65f));*/

        scene->AttachLight(light);
        //scene->AttachLight(light1);
        scene->AttachLight(ibl);

        return scene;
    }
    Material::Ptr SceneIoObj::TranslateMaterialUberV2(ImageIo const& image_io, tinyobj::material_t const& mat, std::string const& basepath, Scene1& scene) const
    {
        auto iter = m_material_cache.find(mat.name);

        if (iter != m_material_cache.cend())
        {
            return iter->second;
        }

        UberV2Material::Ptr material = UberV2Material::Create();

        RadeonRays::float3 emission(mat.emission[0], mat.emission[1], mat.emission[2]);

        bool apply_gamma = false;

        uint32_t material_layers = 0;
        auto uberv2_set_texture = [](UberV2Material::Ptr material, const std::string input_name, Texture::Ptr texture, bool apply_gamma)
        {
            if (apply_gamma)
            {
                material->SetInputValue(input_name.c_str(),
                                    InputMap_Pow::Create(
                                        InputMap_Sampler::Create(texture),
                                        InputMap_ConstantFloat::Create(2.2f)));
            }
            else
            {
                material->SetInputValue(input_name.c_str(), InputMap_Sampler::Create(texture));
            }
        };
        auto uberv2_set_bump_texture = [](UberV2Material::Ptr material, Texture::Ptr texture)
        {
            auto bump_sampler = InputMap_SamplerBumpMap::Create(texture);
            auto bump_remap = Baikal::InputMap_Remap::Create(
                Baikal::InputMap_ConstantFloat3::Create(RadeonRays::float3(0.f, 1.f, 0.f)),
                Baikal::InputMap_ConstantFloat3::Create(RadeonRays::float3(-1.f, 1.f, 0.f)),
                bump_sampler);
                material->SetInputValue("uberv2.shading_normal", bump_remap);

        };

        // Check emission layer
        if (emission.sqnorm() > 0)
        {
            material_layers |= UberV2Material::Layers::kEmissionLayer;
            if (!mat.diffuse_texname.empty())
            {
                auto texture = LoadTexture(image_io, scene, basepath, mat.diffuse_texname);
                uberv2_set_texture(material, "uberv2.emission.color", texture, apply_gamma);
            }
            else
            {
                material->SetInputValue("uberv2.emission.color", InputMap_ConstantFloat3::Create(emission));
            }
        }

        auto s = RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]);
        auto r = RadeonRays::float3(mat.transmittance[0], mat.transmittance[1], mat.transmittance[2]);
        auto d = RadeonRays::float3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2]);

        auto default_ior = Baikal::InputMap_ConstantFloat::Create(3.0f);
        auto default_roughness = Baikal::InputMap_ConstantFloat::Create(0.01f);
        auto default_one = Baikal::InputMap_ConstantFloat::Create(1.0f);

        // Check refraction layer
        if (r.sqnorm() > 0)
        {
            material_layers |= UberV2Material::Layers::kRefractionLayer;
            material->SetInputValue("uberv2.refraction.ior", default_ior);
            material->SetInputValue("uberv2.refraction.roughness", default_roughness);
            material->SetInputValue("uberv2.refraction.color", InputMap_ConstantFloat3::Create(r));
        }

        // Check reflection layer
        if (s.sqnorm() > 0)
        {
            material_layers |= UberV2Material::Layers::kReflectionLayer;
            material->SetInputValue("uberv2.reflection.ior", default_ior);
            material->SetInputValue("uberv2.reflection.roughness", default_roughness);
            material->SetInputValue("uberv2.reflection.metalness", default_one);

            if (!mat.specular_texname.empty())
            {
                auto texture = LoadTexture(image_io, scene, basepath, mat.specular_texname);
                uberv2_set_texture(material, "uberv2.reflection.color", texture, apply_gamma);
            }
            else
            {
                material->SetInputValue("uberv2.reflection.color", InputMap_ConstantFloat3::Create(s));
            }
        }

        // Check if we have bump map
        if (!mat.bump_texname.empty())
        {
            material_layers |= UberV2Material::Layers::kShadingNormalLayer;

            auto texture = LoadTexture(image_io, scene, basepath, mat.bump_texname);
            uberv2_set_bump_texture(material, texture);
        }

        // Finally add diffuse layer
        {
            material_layers |= UberV2Material::Layers::kDiffuseLayer;

            if (!mat.diffuse_texname.empty())
            {
                auto texture = LoadTexture(image_io, scene, basepath, mat.diffuse_texname);
                uberv2_set_texture(material, "uberv2.diffuse.color", texture, apply_gamma);
            }
            else
            {
                material->SetInputValue("uberv2.diffuse.color", InputMap_ConstantFloat3::Create(d));
            }
        }

        // Set material name
        material->SetName(mat.name);
        material->SetLayers(material_layers);

        m_material_cache.emplace(std::make_pair(mat.name, material));

        return material;
    }
}
<commit_msg>Apply gamma by default<commit_after>/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/

#include "scene_io.h"
#include "image_io.h"
#include "SceneGraph/scene1.h"
#include "SceneGraph/shape.h"
#include "SceneGraph/material.h"
#include "SceneGraph/light.h"
#include "SceneGraph/texture.h"
#include "math/mathutils.h"

#include "SceneGraph/uberv2material.h"
#include "SceneGraph/inputmaps.h"

#include <string>
#include <map>
#include <set>
#include <cassert>

#include "Utils/tiny_obj_loader.h"
#include "Utils/log.h"

namespace Baikal
{
    // Obj scene loader
    class SceneIoObj : public SceneIo::Loader
    {
    public:
        // Load scene from file
        Scene1::Ptr LoadScene(std::string const& filename, std::string const& basepath) const override;
        SceneIoObj() : SceneIo::Loader("obj", this)
        {
            SceneIo::RegisterLoader("objm", this);
        }
        ~SceneIoObj()
        {
            SceneIo::UnregisterLoader("objm");
        }

    private:
        Material::Ptr TranslateMaterialUberV2(ImageIo const& image_io, tinyobj::material_t const& mat, std::string const& basepath, Scene1& scene) const;

        mutable std::map<std::string, Material::Ptr> m_material_cache;
    };

    // Create static object to register loader. This object will be used as loader
    static SceneIoObj obj_loader;

    Scene1::Ptr SceneIoObj::LoadScene(std::string const& filename, std::string const& basepath) const
    {
        using namespace tinyobj;

        auto image_io(ImageIo::CreateImageIo());

        // Loader data
        std::vector<shape_t> objshapes;
        std::vector<material_t> objmaterials;

        // Try loading file
        LogInfo("Loading a scene from OBJ: ", filename, " ... ");
        std::string err;
        auto res = LoadObj(objshapes, objmaterials, err, filename.c_str(), basepath.c_str(), triangulation|calculate_normals);
        if (!res)
        {
            throw std::runtime_error(err);
        }
        LogInfo("Success\n");

        // Allocate scene
        auto scene = Scene1::Create();

        // Enumerate and translate materials
        // Keep track of emissive subset
        std::set<Material::Ptr> emissives;
        std::vector<Material::Ptr> materials(objmaterials.size());
        for (int i = 0; i < (int)objmaterials.size(); ++i)
        {
            // Translate material
            materials[i] = TranslateMaterialUberV2(*image_io, objmaterials[i], basepath, *scene);

            // Add to emissive subset if needed
            if (materials[i]->HasEmission())
            {
                emissives.insert(materials[i]);
            }
        }

        // Enumerate all shapes in the scene
        for (int s = 0; s < (int)objshapes.size(); ++s)
        {
            const auto& shape = objshapes[s];

            // Find all materials used by this shape.
            std::set<int> used_materials(std::begin(shape.mesh.material_ids), std::end(shape.mesh.material_ids));

            // Split the mesh into multiple meshes, each with only one material.
            for (int used_material : used_materials)
            {
                // Map from old index to new index.
                std::map<unsigned int, unsigned int> used_indices;

                // Remapped indices.
                std::vector<unsigned int> indices;

                // Collected vertex/normal/texcoord data.
                std::vector<float> vertices, normals, texcoords;

                // Go through each face in the mesh.
                for (size_t i = 0; i < shape.mesh.material_ids.size(); ++i)
                {
                    // Skip faces which don't use the current material.
                    if (shape.mesh.material_ids[i] != used_material) continue;

                    const int num_face_vertices = shape.mesh.num_vertices[i];
                    assert(num_face_vertices == 3 && "expected triangles");
                    // For each vertex index of this face.
                    for (int j = 0; j < num_face_vertices; ++j)
                    {
                        const unsigned int old_index = shape.mesh.indices[num_face_vertices * i + j];
                        // Collect vertex/normal/texcoord data. Avoid inserting the same data twice.
                        auto result = used_indices.emplace(old_index, (unsigned int)(vertices.size() / 3));
                        if (result.second) // Did insert?
                        {
                            // Push the new data.
                            for (int k = 0; k < 3; ++k)
                                vertices.push_back(shape.mesh.positions[3 * old_index + k]);
                            for (int k = 0; k < 3; ++k)
                                normals.push_back(shape.mesh.normals[3 * old_index + k]);
                            if (!shape.mesh.texcoords.empty())
                                for (int k = 0; k < 2; ++k)
                                    texcoords.push_back(shape.mesh.texcoords[2 * old_index + k]);
                        }
                        const unsigned int new_index = result.first->second;
                        indices.push_back(new_index);
                    }
                }

                // Create empty mesh
                auto mesh = Mesh::Create();

                // Set vertex and index data
                auto num_vertices = vertices.size() / 3;
                mesh->SetVertices(&vertices[0], num_vertices);

                auto num_normals = normals.size() / 3;
                mesh->SetNormals(&normals[0], num_normals);

                auto num_uvs = texcoords.size() / 2;

                // If we do not have UVs, generate zeroes
                if (num_uvs)
                {
                    mesh->SetUVs(&texcoords[0], num_uvs);
                }
                else
                {
                    std::vector<RadeonRays::float2> zero(num_vertices);
                    std::fill(zero.begin(), zero.end(), RadeonRays::float2(0, 0));
                    mesh->SetUVs(&zero[0], num_vertices);
                }

                // Set indices
                auto num_indices = indices.size();
                mesh->SetIndices(reinterpret_cast<std::uint32_t const*>(&indices[0]), num_indices);

                // Set material

                if (used_material >= 0)
                {
                    mesh->SetMaterial(materials[used_material]);
                }

                // Attach to the scene
                scene->AttachShape(mesh);

                // If the mesh has emissive material we need to add area light for it
                if (used_material >= 0 && emissives.find(materials[used_material]) != emissives.cend())
                {
                    // Add area light for each polygon of emissive mesh
                    for (std::size_t l = 0; l < mesh->GetNumIndices() / 3; ++l)
                    {
                        auto light = AreaLight::Create(mesh, l);
                        scene->AttachLight(light);
                    }
                }
            }
        }

        // TODO: temporary code, add IBL
        auto ibl_texture = image_io->LoadImage("../Resources/Textures/studio015.hdr");
        //auto ibl_texture1 = image_io->LoadImage("../Resources/Textures/sky.hdr");

        auto ibl = ImageBasedLight::Create();
        ibl->SetTexture(ibl_texture);
        //ibl->SetReflectionTexture(ibl_texture1);
        //ibl->SetBackgroundTexture(ibl_texture);
        ibl->SetMultiplier(1.f);

        // TODO: temporary code to add directional light
        auto light = DirectionalLight::Create();
        light->SetDirection(RadeonRays::float3(.1f, -1.f, -.1f));
        light->SetEmittedRadiance(RadeonRays::float3(1.f, 1.f, 1.f));

        /*auto light1 = DirectionalLight::Create();
        auto d = RadeonRays::float3(-0.1f, -1.f, -1.f);
        d.normalize();
        light1->SetDirection(d);
        light1->SetEmittedRadiance(3.f * RadeonRays::float3(1.f, 0.8f, 0.65f));*/

        scene->AttachLight(light);
        //scene->AttachLight(light1);
        scene->AttachLight(ibl);

        return scene;
    }
    Material::Ptr SceneIoObj::TranslateMaterialUberV2(ImageIo const& image_io, tinyobj::material_t const& mat, std::string const& basepath, Scene1& scene) const
    {
        auto iter = m_material_cache.find(mat.name);

        if (iter != m_material_cache.cend())
        {
            return iter->second;
        }

        UberV2Material::Ptr material = UberV2Material::Create();

        RadeonRays::float3 emission(mat.emission[0], mat.emission[1], mat.emission[2]);

        bool apply_gamma = true;

        uint32_t material_layers = 0;
        auto uberv2_set_texture = [](UberV2Material::Ptr material, const std::string input_name, Texture::Ptr texture, bool apply_gamma)
        {
            if (apply_gamma)
            {
                material->SetInputValue(input_name.c_str(),
                                    InputMap_Pow::Create(
                                        InputMap_Sampler::Create(texture),
                                        InputMap_ConstantFloat::Create(2.2f)));
            }
            else
            {
                material->SetInputValue(input_name.c_str(), InputMap_Sampler::Create(texture));
            }
        };
        auto uberv2_set_bump_texture = [](UberV2Material::Ptr material, Texture::Ptr texture)
        {
            auto bump_sampler = InputMap_SamplerBumpMap::Create(texture);
            auto bump_remap = Baikal::InputMap_Remap::Create(
                Baikal::InputMap_ConstantFloat3::Create(RadeonRays::float3(0.f, 1.f, 0.f)),
                Baikal::InputMap_ConstantFloat3::Create(RadeonRays::float3(-1.f, 1.f, 0.f)),
                bump_sampler);
                material->SetInputValue("uberv2.shading_normal", bump_remap);

        };

        // Check emission layer
        if (emission.sqnorm() > 0)
        {
            material_layers |= UberV2Material::Layers::kEmissionLayer;
            if (!mat.diffuse_texname.empty())
            {
                auto texture = LoadTexture(image_io, scene, basepath, mat.diffuse_texname);
                uberv2_set_texture(material, "uberv2.emission.color", texture, apply_gamma);
            }
            else
            {
                material->SetInputValue("uberv2.emission.color", InputMap_ConstantFloat3::Create(emission));
            }
        }

        auto s = RadeonRays::float3(mat.specular[0], mat.specular[1], mat.specular[2]);
        auto r = RadeonRays::float3(mat.transmittance[0], mat.transmittance[1], mat.transmittance[2]);
        auto d = RadeonRays::float3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2]);

        auto default_ior = Baikal::InputMap_ConstantFloat::Create(3.0f);
        auto default_roughness = Baikal::InputMap_ConstantFloat::Create(0.01f);
        auto default_one = Baikal::InputMap_ConstantFloat::Create(1.0f);

        // Check refraction layer
        if (r.sqnorm() > 0)
        {
            material_layers |= UberV2Material::Layers::kRefractionLayer;
            material->SetInputValue("uberv2.refraction.ior", default_ior);
            material->SetInputValue("uberv2.refraction.roughness", default_roughness);
            material->SetInputValue("uberv2.refraction.color", InputMap_ConstantFloat3::Create(r));
        }

        // Check reflection layer
        if (s.sqnorm() > 0)
        {
            material_layers |= UberV2Material::Layers::kReflectionLayer;
            material->SetInputValue("uberv2.reflection.ior", default_ior);
            material->SetInputValue("uberv2.reflection.roughness", default_roughness);
            material->SetInputValue("uberv2.reflection.metalness", default_one);

            if (!mat.specular_texname.empty())
            {
                auto texture = LoadTexture(image_io, scene, basepath, mat.specular_texname);
                uberv2_set_texture(material, "uberv2.reflection.color", texture, apply_gamma);
            }
            else
            {
                material->SetInputValue("uberv2.reflection.color", InputMap_ConstantFloat3::Create(s));
            }
        }

        // Check if we have bump map
        if (!mat.bump_texname.empty())
        {
            material_layers |= UberV2Material::Layers::kShadingNormalLayer;

            auto texture = LoadTexture(image_io, scene, basepath, mat.bump_texname);
            uberv2_set_bump_texture(material, texture);
        }

        // Finally add diffuse layer
        {
            material_layers |= UberV2Material::Layers::kDiffuseLayer;

            if (!mat.diffuse_texname.empty())
            {
                auto texture = LoadTexture(image_io, scene, basepath, mat.diffuse_texname);
                uberv2_set_texture(material, "uberv2.diffuse.color", texture, apply_gamma);
            }
            else
            {
                material->SetInputValue("uberv2.diffuse.color", InputMap_ConstantFloat3::Create(d));
            }
        }

        // Set material name
        material->SetName(mat.name);
        material->SetLayers(material_layers);

        m_material_cache.emplace(std::make_pair(mat.name, material));

        return material;
    }
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_FUN_UNIT_VECTOR_CONSTRAIN_HPP
#define STAN_MATH_PRIM_FUN_UNIT_VECTOR_CONSTRAIN_HPP

#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/fun/dot_self.hpp>
#include <stan/math/prim/fun/sqrt.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
 * Return the unit length vector corresponding to the free vector y.
 *
 * See the
 * Wikipedia page on generating random points on an N-sphere.
 *
 * @tparam EigMat type inheriting from `EigenBase` that does not have an fvar
 *  scalar type.
 * @param y vector of K unrestricted variables
 * @return Unit length vector of dimension K
 */
template <typename T, require_eigen_col_vector_t<T>* = nullptr,
          require_not_vt_autodiff<T>* = nullptr>
inline auto unit_vector_constrain(const T& y) {
  using std::sqrt;
  check_nonzero_size("unit_vector_constrain", "y", y);
  return make_holder(
      [](const auto& y_ref) {
        value_type_t<T> SN = dot_self(y_ref);
        check_positive_finite("unit_vector_constrain", "norm", SN);
        return y_ref / sqrt(SN);
      },
      to_ref(y));
}

/**
 * Return the unit length vector corresponding to the free vector y.
 * See https://en.wikipedia.org/wiki/N-sphere#Generating_random_points
 *
 * @tparam EigMat type inheriting from `EigenBase` that does not have an fvar
 *  scalar type.
 *
 * @param y vector of K unrestricted variables
 * @return Unit length vector of dimension K
 * @param lp Log probability reference to increment.
 */
template <typename T1, typename T2, require_eigen_col_vector_t<T1>* = nullptr,
          require_all_not_vt_autodiff<T1, T2>* = nullptr>
inline plain_type_t<T1> unit_vector_constrain(const T1& y, T2& lp) {
  using std::sqrt;
  check_nonzero_size("unit_vector_constrain", "y", y);
  return make_holder(
      [](const auto& y_ref, const auto& lp) {
        value_type_t<T1> SN = dot_self(y_ref);
        check_positive_finite("unit_vector_constrain", "norm", SN);
        lp -= 0.5 * SN;
        return y_ref / sqrt(SN);
      },
      to_ref(y), lp);
}

}  // namespace math
}  // namespace stan

#endif
<commit_msg>fix unit vector constrain<commit_after>#ifndef STAN_MATH_PRIM_FUN_UNIT_VECTOR_CONSTRAIN_HPP
#define STAN_MATH_PRIM_FUN_UNIT_VECTOR_CONSTRAIN_HPP

#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/fun/dot_self.hpp>
#include <stan/math/prim/fun/sqrt.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
 * Return the unit length vector corresponding to the free vector y.
 *
 * See the
 * Wikipedia page on generating random points on an N-sphere.
 *
 * @tparam EigMat type inheriting from `EigenBase` that does not have an fvar
 *  scalar type.
 * @param y vector of K unrestricted variables
 * @return Unit length vector of dimension K
 */
template <typename T, require_eigen_col_vector_t<T>* = nullptr,
          require_not_vt_autodiff<T>* = nullptr>
inline auto unit_vector_constrain(const T& y) {
  using std::sqrt;
  check_nonzero_size("unit_vector_constrain", "y", y);
  return make_holder(
      [](const auto& y_ref) {
        value_type_t<T> SN = dot_self(y_ref);
        check_positive_finite("unit_vector_constrain", "norm", SN);
        return y_ref / sqrt(SN);
      },
      to_ref(y));
}

/**
 * Return the unit length vector corresponding to the free vector y.
 * See https://en.wikipedia.org/wiki/N-sphere#Generating_random_points
 *
 * @tparam EigMat type inheriting from `EigenBase` that does not have an fvar
 *  scalar type.
 *
 * @param y vector of K unrestricted variables
 * @return Unit length vector of dimension K
 * @param lp Log probability reference to increment.
 */
template <typename T1, typename T2, require_eigen_col_vector_t<T1>* = nullptr,
          require_all_not_vt_autodiff<T1, T2>* = nullptr>
inline plain_type_t<T1> unit_vector_constrain(const T1& y, T2& lp) {
  using std::sqrt;
  check_nonzero_size("unit_vector_constrain", "y", y);
  return make_holder(
      [](const auto& y_ref, auto& lp) {
        value_type_t<T1> SN = dot_self(y_ref);
        check_positive_finite("unit_vector_constrain", "norm", SN);
        lp -= 0.5 * SN;
        return y_ref / sqrt(SN);
      },
      to_ref(y), lp);
}

}  // namespace math
}  // namespace stan

#endif
<|endoftext|>