<commit_before>#include "reGlobalsExtern.hpp"
#include "dataObjChksum.hpp"
#include "dataObjRsync.hpp"
#include "objMetaOpr.hpp"
#include "specColl.hpp"
#include "dataObjOpr.hpp"
#include "rsApiHandler.hpp"
#include "modDataObjMeta.hpp"
#include "getRemoteZoneResc.hpp"

#include "irods_resource_redirect.hpp"

int
rsDataObjRsync( rsComm_t *rsComm, dataObjInp_t *dataObjInp,
                msParamArray_t **outParamArray ) {
    int status;
    char *rsyncMode;
    char *remoteZoneOpr;
    int remoteFlag;
    rodsServerHost_t *rodsServerHost;
    specCollCache_t *specCollCache = NULL;

    *outParamArray = NULL;
    if ( dataObjInp == NULL ) {
        rodsLog( LOG_ERROR, "rsDataObjRsync error. NULL input" );
        return SYS_INTERNAL_NULL_INPUT_ERR;
    }

    rsyncMode = getValByKey( &dataObjInp->condInput, RSYNC_MODE_KW );
    if ( rsyncMode == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsDataObjRsync: RSYNC_MODE_KW input is missing" );
        return USER_RSYNC_NO_MODE_INPUT_ERR;
    }

    if ( strcmp( rsyncMode, LOCAL_TO_IRODS ) == 0 ) {
        remoteZoneOpr = REMOTE_CREATE;
    }
    else {
        remoteZoneOpr = REMOTE_OPEN;
    }

    resolveLinkedPath( rsComm, dataObjInp->objPath, &specCollCache,
                       &dataObjInp->condInput );
    if ( strcmp( rsyncMode, IRODS_TO_IRODS ) == 0 ) {
        if ( isLocalZone( dataObjInp->objPath ) == 0 ) {
            dataObjInp_t myDataObjInp;
            char *destObjPath;
            /* source in a remote zone. try dest */
            destObjPath = getValByKey( &dataObjInp->condInput,
                                       RSYNC_DEST_PATH_KW );
            if ( destObjPath == NULL ) {
                rodsLog( LOG_ERROR,
                         "rsDataObjRsync: RSYNC_DEST_PATH_KW input is missing for %s",
                         dataObjInp->objPath );
                return USER_RSYNC_NO_MODE_INPUT_ERR;
            }
            myDataObjInp = *dataObjInp;
            remoteZoneOpr = REMOTE_CREATE;
            rstrcpy( myDataObjInp.objPath, destObjPath, MAX_NAME_LEN );
            remoteFlag = getAndConnRemoteZone( rsComm, &myDataObjInp,
                                               &rodsServerHost, remoteZoneOpr );
        }
        else {
            remoteFlag = getAndConnRemoteZone( rsComm, dataObjInp,
                                               &rodsServerHost, remoteZoneOpr );
        }
    }
    else {
        remoteFlag = getAndConnRemoteZone( rsComm, dataObjInp, &rodsServerHost,
                                           remoteZoneOpr );
    }

    if ( remoteFlag < 0 ) {
        return remoteFlag;
    }
    else if ( remoteFlag == REMOTE_HOST ) {

        status = _rcDataObjRsync( rodsServerHost->conn, dataObjInp,
                                  outParamArray );
        return status;
    }

    if ( strcmp( rsyncMode, IRODS_TO_LOCAL ) == 0 ) {
        status = rsRsyncFileToData( rsComm, dataObjInp );
    }
    else if ( strcmp( rsyncMode, LOCAL_TO_IRODS ) == 0 ) {
        status = rsRsyncDataToFile( rsComm, dataObjInp );
    }
    else if ( strcmp( rsyncMode, IRODS_TO_IRODS ) == 0 ) {
        status = rsRsyncDataToData( rsComm, dataObjInp );
    }
    else {
        rodsLog( LOG_ERROR,
                 "rsDataObjRsync: rsyncMode %s  not supported" );
        return USER_RSYNC_NO_MODE_INPUT_ERR;
    }

    return status;
}

int
rsRsyncDataToFile( rsComm_t *rsComm, dataObjInp_t *dataObjInp ) {
    int status;
    char *fileChksumStr = NULL;
    char *dataObjChksumStr = NULL;
    dataObjInfo_t *dataObjInfoHead = NULL;

    fileChksumStr = getValByKey( &dataObjInp->condInput, RSYNC_CHKSUM_KW );

    if ( fileChksumStr == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToFile: RSYNC_CHKSUM_KW input is missing for %s",
                 dataObjInp->objPath );
        return CHKSUM_EMPTY_IN_STRUCT_ERR;
    }

    status = _rsDataObjChksum( rsComm, dataObjInp, &dataObjChksumStr,
                               &dataObjInfoHead );

    if ( status < 0 && status != CAT_NO_ACCESS_PERMISSION &&
            status != CAT_NO_ROWS_FOUND ) {
        /* XXXXX CAT_NO_ACCESS_PERMISSION mean the chksum was calculated but
         * cannot be registered. But the chksum value is OK.
         */
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToFile: _rsDataObjChksum of %s error. status = %d",
                 dataObjInp->objPath, status );
        return status;
    }

    freeAllDataObjInfo( dataObjInfoHead );

    if ( dataObjChksumStr != NULL &&
            strcmp( dataObjChksumStr, fileChksumStr ) == 0 ) {
        free( dataObjChksumStr );
        return 0;
    }

    return SYS_SVR_TO_CLI_GET_ACTION;
}

int
rsRsyncFileToData( rsComm_t *rsComm, dataObjInp_t *dataObjInp ) {
    int status;
    char *fileChksumStr = NULL;
    char *dataObjChksumStr = NULL;
    dataObjInfo_t *dataObjInfoHead = NULL;

    fileChksumStr = getValByKey( &dataObjInp->condInput, RSYNC_CHKSUM_KW );

    if ( fileChksumStr == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncFileToData: RSYNC_CHKSUM_KW input is missing" );
        return CHKSUM_EMPTY_IN_STRUCT_ERR;
    }

    // =-=-=-=-=-=-=-
    // determine the resource hierarchy if one is not provided
    if ( getValByKey( &dataObjInp->condInput, RESC_HIER_STR_KW ) == NULL ) {
        std::string       hier;
        irods::error ret = irods::resolve_resource_hierarchy( irods::OPEN_OPERATION,
                           rsComm, dataObjInp, hier );
        if ( !ret.ok() ) {
            std::stringstream msg;
            msg << __FUNCTION__;
            msg << " :: failed in irods::resolve_resource_hierarchy for [";
            msg << dataObjInp->objPath << "]";
            irods::log( PASSMSG( msg.str(), ret ) );
            return ret.code();
        }

        // =-=-=-=-=-=-=-
        // we resolved the redirect and have a host, set the hier str for subsequent
        // api calls, etc.
        addKeyVal( &dataObjInp->condInput, RESC_HIER_STR_KW, hier.c_str() );

    } // if keyword

    status = _rsDataObjChksum( rsComm, dataObjInp, &dataObjChksumStr,
                               &dataObjInfoHead );

    if ( status < 0 && status != CAT_NO_ACCESS_PERMISSION &&
            status != CAT_NO_ROWS_FOUND ) {
        /* XXXXX CAT_NO_ACCESS_PERMISSION mean the chksum was calculated but
         * cannot be registered. But the chksum value is OK.
         */
        rodsLog( LOG_ERROR,
                 "rsRsyncFileToData: _rsDataObjChksum of %s error. status = %d",
                 dataObjInp->objPath, status );
    }

    freeAllDataObjInfo( dataObjInfoHead );

    if ( dataObjChksumStr != NULL &&
            strcmp( dataObjChksumStr, fileChksumStr ) == 0 ) {
        free( dataObjChksumStr );
        return 0;
    }
    return SYS_SVR_TO_CLI_PUT_ACTION;
}

int
rsRsyncDataToData( rsComm_t *rsComm, dataObjInp_t *dataObjInp ) {
    int status;
    char *srcChksumStr = NULL;
    char *destChksumStr = NULL;
    dataObjCopyInp_t dataObjCopyInp;
    char *destObjPath;
    transferStat_t *transStat = NULL;

    /* always have the FORCE flag on */
    addKeyVal( &dataObjInp->condInput, FORCE_FLAG_KW, "" );

    destObjPath = getValByKey( &dataObjInp->condInput, RSYNC_DEST_PATH_KW );
    if ( destObjPath == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToData: RSYNC_DEST_PATH_KW input is missing for %s",
                 dataObjInp->objPath );
        return USER_RSYNC_NO_MODE_INPUT_ERR;
    }

    memset( &dataObjCopyInp, 0, sizeof( dataObjCopyInp ) );
    rstrcpy( dataObjCopyInp.srcDataObjInp.objPath, dataObjInp->objPath,
             MAX_NAME_LEN );
    dataObjCopyInp.srcDataObjInp.dataSize = dataObjInp->dataSize;
    replDataObjInp( dataObjInp, &dataObjCopyInp.destDataObjInp );
    rstrcpy( dataObjCopyInp.destDataObjInp.objPath, destObjPath,
             MAX_NAME_LEN );

    /* use rsDataObjChksum because the path could in in remote zone */
    status = rsDataObjChksum( rsComm, &dataObjCopyInp.srcDataObjInp,
                              &srcChksumStr );

    if ( status < 0 &&
            ( status != CAT_NO_ACCESS_PERMISSION || srcChksumStr == NULL ) ) {
        /* XXXXX CAT_NO_ACCESS_PERMISSION mean the chksum was calculated but
         * cannot be registered. But the chksum value is OK.
         */
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToData: _rsDataObjChksum error for %s, status = %d",
                 dataObjCopyInp.srcDataObjInp.objPath, status );
        clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );
        return status;
    }

    /* use rsDataObjChksum because the path could in in remote zone */
    status = rsDataObjChksum( rsComm, &dataObjCopyInp.destDataObjInp,
                              &destChksumStr );

    if ( status < 0 && status != CAT_NO_ACCESS_PERMISSION &&
            status != CAT_NO_ROWS_FOUND ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToData: _rsDataObjChksum error for %s, status = %d",
                 dataObjCopyInp.destDataObjInp.objPath, status );
        free( srcChksumStr );
        free( destChksumStr );
        clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );
        clearKeyVal( &dataObjCopyInp.destDataObjInp.condInput );
        return status;
    }

    if ( destChksumStr != NULL && strcmp( srcChksumStr, destChksumStr ) == 0 ) {
        free( srcChksumStr );
        free( destChksumStr );
        clearKeyVal( &dataObjCopyInp.destDataObjInp.condInput );
        clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );
        return 0;
    }

    addKeyVal( &dataObjCopyInp.destDataObjInp.condInput, REG_CHKSUM_KW,
               srcChksumStr );
    status = rsDataObjCopy( rsComm, &dataObjCopyInp, &transStat );
    free( transStat );
    free( srcChksumStr );
    free( destChksumStr );
    clearKeyVal( &dataObjCopyInp.destDataObjInp.condInput );
    clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );

    if ( status >= 0 ) {
        return SYS_RSYNC_TARGET_MODIFIED;
    }
    else {
        return status;
    }
}

<commit_msg>[#2212] CID25831:<commit_after>#include "reGlobalsExtern.hpp"
#include "dataObjChksum.hpp"
#include "dataObjRsync.hpp"
#include "objMetaOpr.hpp"
#include "specColl.hpp"
#include "dataObjOpr.hpp"
#include "rsApiHandler.hpp"
#include "modDataObjMeta.hpp"
#include "getRemoteZoneResc.hpp"

#include "irods_resource_redirect.hpp"

int
rsDataObjRsync( rsComm_t *rsComm, dataObjInp_t *dataObjInp,
                msParamArray_t **outParamArray ) {
    int status;
    char *rsyncMode;
    char *remoteZoneOpr;
    int remoteFlag;
    rodsServerHost_t *rodsServerHost;
    specCollCache_t *specCollCache = NULL;

    *outParamArray = NULL;
    if ( dataObjInp == NULL ) {
        rodsLog( LOG_ERROR, "rsDataObjRsync error. NULL input" );
        return SYS_INTERNAL_NULL_INPUT_ERR;
    }

    rsyncMode = getValByKey( &dataObjInp->condInput, RSYNC_MODE_KW );
    if ( rsyncMode == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsDataObjRsync: RSYNC_MODE_KW input is missing" );
        return USER_RSYNC_NO_MODE_INPUT_ERR;
    }

    if ( strcmp( rsyncMode, LOCAL_TO_IRODS ) == 0 ) {
        remoteZoneOpr = REMOTE_CREATE;
    }
    else {
        remoteZoneOpr = REMOTE_OPEN;
    }

    resolveLinkedPath( rsComm, dataObjInp->objPath, &specCollCache,
                       &dataObjInp->condInput );
    if ( strcmp( rsyncMode, IRODS_TO_IRODS ) == 0 ) {
        if ( isLocalZone( dataObjInp->objPath ) == 0 ) {
            dataObjInp_t myDataObjInp;
            char *destObjPath;
            /* source in a remote zone. try dest */
            destObjPath = getValByKey( &dataObjInp->condInput,
                                       RSYNC_DEST_PATH_KW );
            if ( destObjPath == NULL ) {
                rodsLog( LOG_ERROR,
                         "rsDataObjRsync: RSYNC_DEST_PATH_KW input is missing for %s",
                         dataObjInp->objPath );
                return USER_RSYNC_NO_MODE_INPUT_ERR;
            }
            myDataObjInp = *dataObjInp;
            remoteZoneOpr = REMOTE_CREATE;
            rstrcpy( myDataObjInp.objPath, destObjPath, MAX_NAME_LEN );
            remoteFlag = getAndConnRemoteZone( rsComm, &myDataObjInp,
                                               &rodsServerHost, remoteZoneOpr );
        }
        else {
            remoteFlag = getAndConnRemoteZone( rsComm, dataObjInp,
                                               &rodsServerHost, remoteZoneOpr );
        }
    }
    else {
        remoteFlag = getAndConnRemoteZone( rsComm, dataObjInp, &rodsServerHost,
                                           remoteZoneOpr );
    }

    if ( remoteFlag < 0 ) {
        return remoteFlag;
    }
    else if ( remoteFlag == REMOTE_HOST ) {

        status = _rcDataObjRsync( rodsServerHost->conn, dataObjInp,
                                  outParamArray );
        return status;
    }

    if ( strcmp( rsyncMode, IRODS_TO_LOCAL ) == 0 ) {
        status = rsRsyncFileToData( rsComm, dataObjInp );
    }
    else if ( strcmp( rsyncMode, LOCAL_TO_IRODS ) == 0 ) {
        status = rsRsyncDataToFile( rsComm, dataObjInp );
    }
    else if ( strcmp( rsyncMode, IRODS_TO_IRODS ) == 0 ) {
        status = rsRsyncDataToData( rsComm, dataObjInp );
    }
    else {
        rodsLog( LOG_ERROR,
                 "rsDataObjRsync: rsyncMode %s  not supported" );
        return USER_RSYNC_NO_MODE_INPUT_ERR;
    }

    return status;
}

int
rsRsyncDataToFile( rsComm_t *rsComm, dataObjInp_t *dataObjInp ) {
    int status;
    char *fileChksumStr = NULL;
    char *dataObjChksumStr = NULL;
    dataObjInfo_t *dataObjInfoHead = NULL;

    fileChksumStr = getValByKey( &dataObjInp->condInput, RSYNC_CHKSUM_KW );

    if ( fileChksumStr == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToFile: RSYNC_CHKSUM_KW input is missing for %s",
                 dataObjInp->objPath );
        return CHKSUM_EMPTY_IN_STRUCT_ERR;
    }

    status = _rsDataObjChksum( rsComm, dataObjInp, &dataObjChksumStr,
                               &dataObjInfoHead );

    if ( status < 0 && status != CAT_NO_ACCESS_PERMISSION &&
            status != CAT_NO_ROWS_FOUND ) {
        /* XXXXX CAT_NO_ACCESS_PERMISSION mean the chksum was calculated but
         * cannot be registered. But the chksum value is OK.
         */
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToFile: _rsDataObjChksum of %s error. status = %d",
                 dataObjInp->objPath, status );
        return status;
    }

    freeAllDataObjInfo( dataObjInfoHead );

    if ( dataObjChksumStr != NULL &&
            strcmp( dataObjChksumStr, fileChksumStr ) == 0 ) {
        free( dataObjChksumStr );
        return 0;
    }

    return SYS_SVR_TO_CLI_GET_ACTION;
}

int
rsRsyncFileToData( rsComm_t *rsComm, dataObjInp_t *dataObjInp ) {

    char * fileChksumStr = getValByKey( &dataObjInp->condInput, RSYNC_CHKSUM_KW );

    if ( fileChksumStr == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncFileToData: RSYNC_CHKSUM_KW input is missing" );
        return CHKSUM_EMPTY_IN_STRUCT_ERR;
    }

    // =-=-=-=-=-=-=-
    // determine the resource hierarchy if one is not provided
    if ( getValByKey( &dataObjInp->condInput, RESC_HIER_STR_KW ) == NULL ) {
        std::string       hier;
        irods::error ret = irods::resolve_resource_hierarchy( irods::OPEN_OPERATION,
                           rsComm, dataObjInp, hier );
        if ( !ret.ok() ) {
            std::stringstream msg;
            msg << __FUNCTION__;
            msg << " :: failed in irods::resolve_resource_hierarchy for [";
            msg << dataObjInp->objPath << "]";
            irods::log( PASSMSG( msg.str(), ret ) );
            return ret.code();
        }

        // =-=-=-=-=-=-=-
        // we resolved the redirect and have a host, set the hier str for subsequent
        // api calls, etc.
        addKeyVal( &dataObjInp->condInput, RESC_HIER_STR_KW, hier.c_str() );

    } // if keyword

    char *dataObjChksumStr = NULL;
    dataObjInfo_t *dataObjInfoHead = NULL;
    int status = _rsDataObjChksum( rsComm, dataObjInp, &dataObjChksumStr,
                               &dataObjInfoHead );

    if ( status < 0 && status != CAT_NO_ACCESS_PERMISSION &&
            status != CAT_NO_ROWS_FOUND ) {
        /* XXXXX CAT_NO_ACCESS_PERMISSION mean the chksum was calculated but
         * cannot be registered. But the chksum value is OK.
         */
        rodsLog( LOG_ERROR,
                 "rsRsyncFileToData: _rsDataObjChksum of %s error. status = %d",
                 dataObjInp->objPath, status );
    }

    freeAllDataObjInfo( dataObjInfoHead );

    if ( dataObjChksumStr != NULL &&
            strcmp( dataObjChksumStr, fileChksumStr ) == 0 ) {
        free( dataObjChksumStr );
        return 0;
    }
    free( dataObjChksumStr );
    return SYS_SVR_TO_CLI_PUT_ACTION;
}

int
rsRsyncDataToData( rsComm_t *rsComm, dataObjInp_t *dataObjInp ) {
    int status;
    char *srcChksumStr = NULL;
    char *destChksumStr = NULL;
    dataObjCopyInp_t dataObjCopyInp;
    char *destObjPath;
    transferStat_t *transStat = NULL;

    /* always have the FORCE flag on */
    addKeyVal( &dataObjInp->condInput, FORCE_FLAG_KW, "" );

    destObjPath = getValByKey( &dataObjInp->condInput, RSYNC_DEST_PATH_KW );
    if ( destObjPath == NULL ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToData: RSYNC_DEST_PATH_KW input is missing for %s",
                 dataObjInp->objPath );
        return USER_RSYNC_NO_MODE_INPUT_ERR;
    }

    memset( &dataObjCopyInp, 0, sizeof( dataObjCopyInp ) );
    rstrcpy( dataObjCopyInp.srcDataObjInp.objPath, dataObjInp->objPath,
             MAX_NAME_LEN );
    dataObjCopyInp.srcDataObjInp.dataSize = dataObjInp->dataSize;
    replDataObjInp( dataObjInp, &dataObjCopyInp.destDataObjInp );
    rstrcpy( dataObjCopyInp.destDataObjInp.objPath, destObjPath,
             MAX_NAME_LEN );

    /* use rsDataObjChksum because the path could in in remote zone */
    status = rsDataObjChksum( rsComm, &dataObjCopyInp.srcDataObjInp,
                              &srcChksumStr );

    if ( status < 0 &&
            ( status != CAT_NO_ACCESS_PERMISSION || srcChksumStr == NULL ) ) {
        /* XXXXX CAT_NO_ACCESS_PERMISSION mean the chksum was calculated but
         * cannot be registered. But the chksum value is OK.
         */
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToData: _rsDataObjChksum error for %s, status = %d",
                 dataObjCopyInp.srcDataObjInp.objPath, status );
        clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );
        return status;
    }

    /* use rsDataObjChksum because the path could in in remote zone */
    status = rsDataObjChksum( rsComm, &dataObjCopyInp.destDataObjInp,
                              &destChksumStr );

    if ( status < 0 && status != CAT_NO_ACCESS_PERMISSION &&
            status != CAT_NO_ROWS_FOUND ) {
        rodsLog( LOG_ERROR,
                 "rsRsyncDataToData: _rsDataObjChksum error for %s, status = %d",
                 dataObjCopyInp.destDataObjInp.objPath, status );
        free( srcChksumStr );
        free( destChksumStr );
        clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );
        clearKeyVal( &dataObjCopyInp.destDataObjInp.condInput );
        return status;
    }

    if ( destChksumStr != NULL && strcmp( srcChksumStr, destChksumStr ) == 0 ) {
        free( srcChksumStr );
        free( destChksumStr );
        clearKeyVal( &dataObjCopyInp.destDataObjInp.condInput );
        clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );
        return 0;
    }

    addKeyVal( &dataObjCopyInp.destDataObjInp.condInput, REG_CHKSUM_KW,
               srcChksumStr );
    status = rsDataObjCopy( rsComm, &dataObjCopyInp, &transStat );
    free( transStat );
    free( srcChksumStr );
    free( destChksumStr );
    clearKeyVal( &dataObjCopyInp.destDataObjInp.condInput );
    clearKeyVal( &dataObjCopyInp.srcDataObjInp.condInput );

    if ( status >= 0 ) {
        return SYS_RSYNC_TARGET_MODIFIED;
    }
    else {
        return status;
    }
}

<|endoftext|>


<commit_before>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Graphics/CullingList.hpp>
#include <Nazara/Graphics/Debug.hpp>

namespace Nz
{
	template<typename T>
	CullingList<T>::~CullingList()
	{
		OnCullingListRelease(this);
	}

	template<typename T>
	std::size_t CullingList<T>::Cull(const Frustumf& frustum, bool* forceInvalidation)
	{
		m_results.clear();

		bool forcedInvalidation = false;

		std::size_t visibleHash = 0U;

		for (NoTestVisibilityEntry& entry : m_noTestList)
		{
			m_results.push_back(entry.renderable);
			Nz::HashCombine(visibleHash, entry.renderable);

			if (entry.forceInvalidation)
			{
				forcedInvalidation = true;
				entry.forceInvalidation = false;
			}
		}

		for (SphereVisibilityEntry& entry : m_sphereTestList)
		{
			if (frustum.Contains(entry.sphere))
			{
				m_results.push_back(entry.renderable);
				Nz::HashCombine(visibleHash, entry.renderable);

				if (entry.forceInvalidation)
				{
					forcedInvalidation = true;
					entry.forceInvalidation = false;
				}
			}
		}

		for (VolumeVisibilityEntry& entry : m_volumeTestList)
		{
			if (frustum.Contains(entry.volume))
			{
				m_results.push_back(entry.renderable);
				Nz::HashCombine(visibleHash, entry.renderable);

				if (entry.forceInvalidation)
				{
					forcedInvalidation = true;
					entry.forceInvalidation = false;
				}
			}
		}

		if (forceInvalidation)
			*forceInvalidation = forcedInvalidation;

		return visibleHash;
	}

	template<typename T>
	typename CullingList<T>::NoTestEntry CullingList<T>::RegisterNoTest(const T* renderable)
	{
		NoTestEntry entry(this, m_noTestList.size());
		m_noTestList.emplace_back(NoTestVisibilityEntry{&entry, renderable, false}); //< Address of entry will be updated when moving

		return entry;
	}

	template<typename T>
	typename CullingList<T>::SphereEntry CullingList<T>::RegisterSphereTest(const T* renderable)
	{
		SphereEntry entry(this, m_sphereTestList.size());
		m_sphereTestList.emplace_back(SphereVisibilityEntry{Nz::Spheref(), &entry, renderable, false}); //< Address of entry will be updated when moving

		return entry;
	}

	template<typename T>
	typename CullingList<T>::VolumeEntry CullingList<T>::RegisterVolumeTest(const T* renderable)
	{
		VolumeEntry entry(this, m_volumeTestList.size());
		m_volumeTestList.emplace_back(VolumeVisibilityEntry{Nz::BoundingVolumef(), &entry, renderable, false}); //< Address of entry will be updated when moving

		return entry;
	}

	// Interface STD
	template<typename T>
	typename CullingList<T>::ResultContainer::iterator CullingList<T>::begin()
	{
		return m_results.begin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::begin() const
	{
		return m_results.begin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::cbegin() const
	{
		return m_results.cbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::cend() const
	{
		return m_results.cend();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::crbegin() const
	{
		return m_results.crbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::crend() const
	{
		return m_results.crend();
	}

	template<typename T>
	bool CullingList<T>::empty() const
	{
		return m_results.empty();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::iterator CullingList<T>::end()
	{
		return m_results.end();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::end() const
	{
		return m_results.end();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::reverse_iterator CullingList<T>::rbegin()
	{
		return m_results.rbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::rbegin() const
	{
		return m_results.rbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::reverse_iterator CullingList<T>::rend()
	{
		return m_results.rend();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::rend() const
	{
		return m_results.rend();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::size_type CullingList<T>::size() const
	{
		return m_results.size();
	}

	template<typename T>
	void CullingList<T>::NotifyForceInvalidation(CullTest type, std::size_t index)
	{
		switch (type)
		{
			case CullTest::NoTest:
			{
				m_noTestList[index].forceInvalidation = true;
				break;
			}

			case CullTest::Sphere:
			{
				m_sphereTestList[index].forceInvalidation = true;
				break;
			}

			case CullTest::Volume:
			{
				m_volumeTestList[index].forceInvalidation = true;
				break;
			}

			default:
				NazaraInternalError("Unhandled culltype");
				break;
		}
	}

	template<typename T>
	void CullingList<T>::NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr)
	{
		switch (type)
		{
			case CullTest::NoTest:
			{
				NoTestVisibilityEntry& entry = m_noTestList[index];
				NazaraAssert(entry.entry == oldPtr, "Invalid entry");

				entry.entry = static_cast<NoTestEntry*>(newPtr);
				break;
			}

			case CullTest::Sphere:
			{
				SphereVisibilityEntry& entry = m_sphereTestList[index];
				NazaraAssert(entry.entry == oldPtr, "Invalid sphere entry");

				entry.entry = static_cast<SphereEntry*>(newPtr);
				break;
			}

			case CullTest::Volume:
			{
				VolumeVisibilityEntry& entry = m_volumeTestList[index];
				NazaraAssert(entry.entry == oldPtr, "Invalid volume entry");

				entry.entry = static_cast<VolumeEntry*>(newPtr);
				break;
			}

			default:
				NazaraInternalError("Unhandled culltype");
				break;
		}
	}

	template<typename T>
	void CullingList<T>::NotifyRelease(CullTest type, std::size_t index)
	{
		switch (type)
		{
			case CullTest::NoTest:
			{
				m_noTestList[index] = std::move(m_noTestList.back());
				m_noTestList[index].entry->UpdateIndex(index);
				m_noTestList.pop_back();
				break;
			}

			case CullTest::Sphere:
			{
				m_sphereTestList[index] = std::move(m_sphereTestList.back());
				m_sphereTestList[index].entry->UpdateIndex(index);
				m_sphereTestList.pop_back();
				break;
			}

			case CullTest::Volume:
			{
				m_volumeTestList[index] = std::move(m_volumeTestList.back());
				m_volumeTestList[index].entry->UpdateIndex(index);
				m_volumeTestList.pop_back();
				break;
			}

			default:
				NazaraInternalError("Unhandled culltype");
				break;
		}
	}

	template<typename T>
	void CullingList<T>::NotifySphereUpdate(std::size_t index, const Spheref& sphere)
	{
		m_sphereTestList[index].sphere = sphere;
	}

	template<typename T>
	void CullingList<T>::NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume)
	{
		m_volumeTestList[index].volume = boundingVolume;
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::Entry() :
	m_parent(nullptr)
	{
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::Entry(CullingList* parent, std::size_t index) :
	m_index(index),
	m_parent(parent)
	{
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::Entry(Entry&& entry) :
	m_index(entry.m_index),
	m_parent(entry.m_parent)
	{
		if (m_parent)
			m_parent->NotifyMovement(Type, m_index, &entry, this);

		entry.m_parent = nullptr;
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::~Entry()
	{
		if (m_parent)
			m_parent->NotifyRelease(Type, m_index);
	}

	template<typename T>
	template<CullTest Type>
	void CullingList<T>::Entry<Type>::ForceInvalidation()
	{
		m_parent->NotifyForceInvalidation(Type, m_index);
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>* CullingList<T>::Entry<Type>::GetParent() const
	{
		return m_parent;
	}

	template<typename T>
	template<CullTest Type>
	void CullingList<T>::Entry<Type>::UpdateIndex(std::size_t index)
	{
		m_index = index;
	}

	template<typename T>
	template<CullTest Type>
	typename CullingList<T>::Entry<Type>& CullingList<T>::Entry<Type>::operator=(Entry&& entry)
	{
		m_index = entry.m_index;
		m_parent = entry.m_parent;
		if (m_parent)
			m_parent->NotifyMovement(Type, m_index, &entry, this);

		entry.m_parent = nullptr;

		return *this;
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	CullingList<T>::NoTestEntry::NoTestEntry() :
	Entry()
	{
	}

	template<typename T>
	CullingList<T>::NoTestEntry::NoTestEntry(CullingList* parent, std::size_t index) :
	Entry(parent, index)
	{
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	CullingList<T>::SphereEntry::SphereEntry() :
	Entry()
	{
	}

	template<typename T>
	CullingList<T>::SphereEntry::SphereEntry(CullingList* parent, std::size_t index) :
	Entry(parent, index)
	{
	}

	template<typename T>
	void CullingList<T>::SphereEntry::UpdateSphere(const Spheref& sphere)
	{
		m_parent->NotifySphereUpdate(m_index, sphere);
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	CullingList<T>::VolumeEntry::VolumeEntry() :
	Entry()
	{
	}

	template<typename T>
	CullingList<T>::VolumeEntry::VolumeEntry(CullingList* parent, std::size_t index) :
	Entry(parent, index)
	{
	}

	template<typename T>
	void Nz::CullingList<T>::VolumeEntry::UpdateVolume(const BoundingVolumef& volume)
	{
		m_parent->NotifyVolumeUpdate(m_index, volume);
	}
}

#include <Nazara/Graphics/DebugOff.hpp>
<commit_msg>Graphics/CullingList: Fix some compilation errors<commit_after>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Graphics/CullingList.hpp>
#include <Nazara/Graphics/Debug.hpp>

namespace Nz
{
	template<typename T>
	CullingList<T>::~CullingList()
	{
		OnCullingListRelease(this);
	}

	template<typename T>
	std::size_t CullingList<T>::Cull(const Frustumf& frustum, bool* forceInvalidation)
	{
		m_results.clear();

		bool forcedInvalidation = false;

		std::size_t visibleHash = 0U;

		for (NoTestVisibilityEntry& entry : m_noTestList)
		{
			m_results.push_back(entry.renderable);
			Nz::HashCombine(visibleHash, entry.renderable);

			if (entry.forceInvalidation)
			{
				forcedInvalidation = true;
				entry.forceInvalidation = false;
			}
		}

		for (SphereVisibilityEntry& entry : m_sphereTestList)
		{
			if (frustum.Contains(entry.sphere))
			{
				m_results.push_back(entry.renderable);
				Nz::HashCombine(visibleHash, entry.renderable);

				if (entry.forceInvalidation)
				{
					forcedInvalidation = true;
					entry.forceInvalidation = false;
				}
			}
		}

		for (VolumeVisibilityEntry& entry : m_volumeTestList)
		{
			if (frustum.Contains(entry.volume))
			{
				m_results.push_back(entry.renderable);
				Nz::HashCombine(visibleHash, entry.renderable);

				if (entry.forceInvalidation)
				{
					forcedInvalidation = true;
					entry.forceInvalidation = false;
				}
			}
		}

		if (forceInvalidation)
			*forceInvalidation = forcedInvalidation;

		return visibleHash;
	}

	template<typename T>
	typename CullingList<T>::NoTestEntry CullingList<T>::RegisterNoTest(const T* renderable)
	{
		NoTestEntry entry(this, m_noTestList.size());
		m_noTestList.emplace_back(NoTestVisibilityEntry{&entry, renderable, false}); //< Address of entry will be updated when moving

		return entry;
	}

	template<typename T>
	typename CullingList<T>::SphereEntry CullingList<T>::RegisterSphereTest(const T* renderable)
	{
		SphereEntry entry(this, m_sphereTestList.size());
		m_sphereTestList.emplace_back(SphereVisibilityEntry{Nz::Spheref(), &entry, renderable, false}); //< Address of entry will be updated when moving

		return entry;
	}

	template<typename T>
	typename CullingList<T>::VolumeEntry CullingList<T>::RegisterVolumeTest(const T* renderable)
	{
		VolumeEntry entry(this, m_volumeTestList.size());
		m_volumeTestList.emplace_back(VolumeVisibilityEntry{Nz::BoundingVolumef(), &entry, renderable, false}); //< Address of entry will be updated when moving

		return entry;
	}

	// Interface STD
	template<typename T>
	typename CullingList<T>::ResultContainer::iterator CullingList<T>::begin()
	{
		return m_results.begin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::begin() const
	{
		return m_results.begin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::cbegin() const
	{
		return m_results.cbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::cend() const
	{
		return m_results.cend();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::crbegin() const
	{
		return m_results.crbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::crend() const
	{
		return m_results.crend();
	}

	template<typename T>
	bool CullingList<T>::empty() const
	{
		return m_results.empty();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::iterator CullingList<T>::end()
	{
		return m_results.end();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::end() const
	{
		return m_results.end();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::reverse_iterator CullingList<T>::rbegin()
	{
		return m_results.rbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::rbegin() const
	{
		return m_results.rbegin();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::reverse_iterator CullingList<T>::rend()
	{
		return m_results.rend();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::rend() const
	{
		return m_results.rend();
	}

	template<typename T>
	typename CullingList<T>::ResultContainer::size_type CullingList<T>::size() const
	{
		return m_results.size();
	}

	template<typename T>
	void CullingList<T>::NotifyForceInvalidation(CullTest type, std::size_t index)
	{
		switch (type)
		{
			case CullTest::NoTest:
			{
				m_noTestList[index].forceInvalidation = true;
				break;
			}

			case CullTest::Sphere:
			{
				m_sphereTestList[index].forceInvalidation = true;
				break;
			}

			case CullTest::Volume:
			{
				m_volumeTestList[index].forceInvalidation = true;
				break;
			}

			default:
				NazaraInternalError("Unhandled culltype");
				break;
		}
	}

	template<typename T>
	void CullingList<T>::NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr)
	{
		switch (type)
		{
			case CullTest::NoTest:
			{
				NoTestVisibilityEntry& entry = m_noTestList[index];
				NazaraAssert(entry.entry == oldPtr, "Invalid entry");

				entry.entry = static_cast<NoTestEntry*>(newPtr);
				break;
			}

			case CullTest::Sphere:
			{
				SphereVisibilityEntry& entry = m_sphereTestList[index];
				NazaraAssert(entry.entry == oldPtr, "Invalid sphere entry");

				entry.entry = static_cast<SphereEntry*>(newPtr);
				break;
			}

			case CullTest::Volume:
			{
				VolumeVisibilityEntry& entry = m_volumeTestList[index];
				NazaraAssert(entry.entry == oldPtr, "Invalid volume entry");

				entry.entry = static_cast<VolumeEntry*>(newPtr);
				break;
			}

			default:
				NazaraInternalError("Unhandled culltype");
				break;
		}
	}

	template<typename T>
	void CullingList<T>::NotifyRelease(CullTest type, std::size_t index)
	{
		switch (type)
		{
			case CullTest::NoTest:
			{
				m_noTestList[index] = std::move(m_noTestList.back());
				m_noTestList[index].entry->UpdateIndex(index);
				m_noTestList.pop_back();
				break;
			}

			case CullTest::Sphere:
			{
				m_sphereTestList[index] = std::move(m_sphereTestList.back());
				m_sphereTestList[index].entry->UpdateIndex(index);
				m_sphereTestList.pop_back();
				break;
			}

			case CullTest::Volume:
			{
				m_volumeTestList[index] = std::move(m_volumeTestList.back());
				m_volumeTestList[index].entry->UpdateIndex(index);
				m_volumeTestList.pop_back();
				break;
			}

			default:
				NazaraInternalError("Unhandled culltype");
				break;
		}
	}

	template<typename T>
	void CullingList<T>::NotifySphereUpdate(std::size_t index, const Spheref& sphere)
	{
		m_sphereTestList[index].sphere = sphere;
	}

	template<typename T>
	void CullingList<T>::NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume)
	{
		m_volumeTestList[index].volume = boundingVolume;
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::Entry() :
	m_parent(nullptr)
	{
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::Entry(CullingList* parent, std::size_t index) :
	m_index(index),
	m_parent(parent)
	{
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::Entry(Entry&& entry) :
	m_index(entry.m_index),
	m_parent(entry.m_parent)
	{
		if (m_parent)
			m_parent->NotifyMovement(Type, m_index, &entry, this);

		entry.m_parent = nullptr;
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>::Entry<Type>::~Entry()
	{
		if (m_parent)
			m_parent->NotifyRelease(Type, m_index);
	}

	template<typename T>
	template<CullTest Type>
	void CullingList<T>::Entry<Type>::ForceInvalidation()
	{
		m_parent->NotifyForceInvalidation(Type, m_index);
	}

	template<typename T>
	template<CullTest Type>
	CullingList<T>* CullingList<T>::Entry<Type>::GetParent() const
	{
		return m_parent;
	}

	template<typename T>
	template<CullTest Type>
	void CullingList<T>::Entry<Type>::UpdateIndex(std::size_t index)
	{
		m_index = index;
	}

	template<typename T>
	template<CullTest Type>
	typename CullingList<T>::Entry<Type>& CullingList<T>::Entry<Type>::operator=(Entry&& entry)
	{
		m_index = entry.m_index;
		m_parent = entry.m_parent;
		if (m_parent)
			m_parent->NotifyMovement(Type, m_index, &entry, this);

		entry.m_parent = nullptr;

		return *this;
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	CullingList<T>::NoTestEntry::NoTestEntry() :
	Entry<CullTest::NoTest>()
	{
	}

	template<typename T>
	CullingList<T>::NoTestEntry::NoTestEntry(CullingList* parent, std::size_t index) :
	Entry<CullTest::NoTest>(parent, index)
	{
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	CullingList<T>::SphereEntry::SphereEntry() :
	Entry<CullTest::Sphere>()
	{
	}

	template<typename T>
	CullingList<T>::SphereEntry::SphereEntry(CullingList* parent, std::size_t index) :
	Entry<CullTest::Sphere>(parent, index)
	{
	}

	template<typename T>
	void CullingList<T>::SphereEntry::UpdateSphere(const Spheref& sphere)
	{
		this->m_parent->NotifySphereUpdate(this->m_index, sphere);
	}

	//////////////////////////////////////////////////////////////////////////

	template<typename T>
	CullingList<T>::VolumeEntry::VolumeEntry() :
	Entry<CullTest::Volume>()
	{
	}

	template<typename T>
	CullingList<T>::VolumeEntry::VolumeEntry(CullingList* parent, std::size_t index) :
	Entry<CullTest::Volume>(parent, index)
	{
	}

	template<typename T>
	void CullingList<T>::VolumeEntry::UpdateVolume(const BoundingVolumef& volume)
	{
		this->m_parent->NotifyVolumeUpdate(this->m_index, volume);
	}
}

#include <Nazara/Graphics/DebugOff.hpp>
<|endoftext|>


<commit_before>/**
 * \file allocator_traits.hpp
 *
 * \brief This header contains the trait type \c allocator_traits that
 *        determines or infers default values for the Allocator concept
 *
 * \author Matthew Rodusek (matthew.rodusek@gmail.com)
 */
#ifndef BIT_MEMORY_ALLOCATORS_ALLOCATOR_TRAITS_HPP
#define BIT_MEMORY_ALLOCATORS_ALLOCATOR_TRAITS_HPP

#include "concepts/Stateless.hpp"         // is_stateless
#include "concepts/Allocator.hpp"         // is_allocator, allocator_has_allocate,
#include "concepts/ExtendedAllocator.hpp" // allocator_has_extended_try_allocate

#include "allocator_reference.hpp" // allocator_reference
#include "allocator_info.hpp"      // allocator_info
#include "errors.hpp"              // get_out_of_memory_handler
#include "macros.hpp"              // BIT_MEMORY_UNUSED
#include "owner.hpp"               // owner

#include <type_traits> // std::true_type, std::false_type, etc
#include <cstddef>     // std::size_t, std::ptrdiff_t
#include <limits>      // std::numeric_limits
#include <memory>      // std::addressof

namespace bit {
  namespace memory {

    //////////////////////////////////////////////////////////////////////////
    /// \brief The allocator_traits class template provides a standardized
    ///        way to access allocator functionality
    ///
    /// This will infer defaults for any optional values that may not be
    /// present in the allocator
    ///
    /// \tparam Allocator the allocator type. Must satisfy Allocator concept
    //////////////////////////////////////////////////////////////////////////
    template<typename Allocator>
    class allocator_traits
    {

      static_assert( is_allocator<Allocator>::value, "Allocator must be an Allocator" );

      //----------------------------------------------------------------------
      // Public Members
      //----------------------------------------------------------------------
    public:

      using size_type       = allocator_size_type_t<Allocator>;
      using difference_type = allocator_difference_type_t<Allocator>;
      using pointer         = allocator_pointer_t<Allocator>;
      using const_pointer   = allocator_const_pointer_t<Allocator>;

      using is_always_equal            = allocator_is_always_equal<Allocator>;
      using is_stateless               = is_stateless<Allocator>;
      using default_alignment          = allocator_default_alignment<Allocator>;
      using max_alignment              = allocator_max_alignment<Allocator>;
      using can_truncate_deallocations = allocator_can_truncate_deallocations<Allocator>;
      using knows_ownership            = allocator_knows_ownership<Allocator>;

      //----------------------------------------------------------------------
      // Allocation
      //----------------------------------------------------------------------
    public:

      /// \brief Attempts to allocate memory of at least \p size bytes,
      ///        aligned to \p align boundary
      ///
      /// On failure, this function returns \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \return the pointer to the allocated memory
      static pointer try_allocate( Allocator& alloc,
                                   size_type size,
                                   size_type align ) noexcept;

      /// \brief Attempts to allocate memory of at least \p size bytes,
      ///        aligned to \p align boundary with an offset of \p offset bytes
      ///
      /// On failure, this function returns \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \param offset the offset of the allocation
      /// \return the pointer to the allocated memory
      template<typename U = Allocator, typename = std::enable_if<is_extended_allocator<U>::value>>
      static pointer try_allocate( Allocator& alloc,
                                   size_type size,
                                   size_type align,
                                   size_type offset ) noexcept;

      /// \brief Allocates memory of at least \p size bytes, aligned to \p
      ///        align boundary
      ///
      /// On failure, this function may throw or invoke the out_of_memory
      /// handler before returning \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \return the pointer to the allocated member
      static pointer allocate( Allocator& alloc,
                               size_type size,
                               size_type align );

      /// \brief Allocates memory of at least \p size bytes, aligned to \p
      ///        align boundary
      ///
      /// On failure, this function may throw or invoke the out_of_memory
      /// handler before returning \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \param offset the offset of the allocation
      /// \return the pointer to the allocated member
      template<typename U = Allocator, typename = std::enable_if<allocator_has_extended_allocate<U>::value>>
      static pointer allocate( Allocator& alloc,
                               size_type size,
                               size_type align,
                               size_type offset );

      //----------------------------------------------------------------------
      // Deallocation
      //----------------------------------------------------------------------

      /// \brief Deallocates a pointer previously allocated with \p allocate
      ///        or try_allocate
      ///
      /// \param alloc the allocator to allocate from
      /// \param p the pointer to deallocate
      /// \param size the size of the allocation
      static void deallocate( Allocator& alloc, pointer p, size_type size );

      //----------------------------------------------------------------------
      // Observers
      //----------------------------------------------------------------------

      /// \brief Checks if the given allocator is known to own the specified
      ///        pointer \p p
      ///
      /// \note This directly invokes \c alloc.owns(p) . It is undefined
      ///       behaviour to invoke this if \c Allocator::owns is not
      ///       defined; branches should instead be taken by using
      ///       \ref knows_ownership
      ///
      /// \param alloc the allocator
      /// \param p the pointer
      static bool owns( Allocator& alloc, const_pointer p ) noexcept;

      /// \brief Gets the name of the specified allocator
      ///
      /// \note Not all allocators are nameable or have a name specified.
      ///       For these allocators, the string returned is "Unnamed"
      ///
      /// \note The lifetime of the pointer returned is unmanaged, and is NOT
      ///       the responsibility of the caller to free.
      ///
      /// \param alloc the allocator to get the name of
      /// \return the name of the allocator
      static allocator_info info( const Allocator& alloc ) noexcept;

      //----------------------------------------------------------------------
      // Capacity
      //----------------------------------------------------------------------

      /// \brief Gets the maximum size allocateable from this allocator
      ///
      /// \note the max_size does not guarantee that the size is able to be
      ///       allocated at the time the value is queried; it just means that
      ///       it is the maximum size that the allocator is capable of
      ///       allocating from an empty state.
      ///
      /// \param alloc the allocator to get the max size from
      /// \return the amount of bytes available for the largest possible
      ///         allocation
      static size_type max_size( const Allocator& alloc ) noexcept;

      /// \brief Gets the minimum size allocateable from this allocator
      ///
      /// \note The default is 1, if this function is not defined
      ///
      /// \param alloc the allocator to get the min size from
      /// \return the minimum amount of bytes able to allocated
      static size_type min_size( const Allocator& alloc ) noexcept;

      //----------------------------------------------------------------------
      // Private Implementation
      //----------------------------------------------------------------------
    private:

      /// \{
      /// \brief Performs the allocation, either by calling
      ///        \c Allocator::allocate, or by calling into
      ///        \c Allocator::try_allocate and defining a boilerplate
      ///        allocate function
      ///
      /// \param alloc the allocator to allocate
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \param offset the offset of the alignment
      /// \return the allocated pointer
      static pointer do_allocate( std::true_type, Allocator& alloc, size_type size, size_type align );
      static pointer do_allocate( std::false_type, Allocator& alloc, size_type size, size_type align );
      static pointer do_allocate( std::true_type, Allocator& alloc, size_type size, size_type align, size_type offset );
      static pointer do_allocate( std::false_type, Allocator& alloc, size_type size, size_type align, size_type offset );
      /// \}

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

      /// \{
      /// \brief Determines the max size of the allocator, either by
      ///        calling \c Allocator::max_size, or by assuming
      ///        \c std::numeric_limits<size_type>::max()
      ///
      /// \param alloc the allocator
      /// \return the max size
      static size_type do_max_size( std::true_type, const Allocator& alloc );
      static size_type do_max_size( std::false_type, const Allocator& alloc );
      /// \}

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

      /// \{
      /// \brief Determines the min size of an allocation, either by
      ///        calling \c Allocator::min_size(), or by assuming
      ///        \c 1
      ///
      /// \param alloc the allocator
      /// \return the max size
      static size_type do_min_size( std::true_type, const Allocator& alloc );
      static size_type do_min_size( std::false_type, const Allocator& alloc );
      /// \}

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

      /// \{
      /// \brief Determines the info for the allocator, either by calling
      ///        \c Allocator::info or by assuming "Unnamed" for the name
      ///
      /// \param alloc the allocator
      /// \return the name of the allocator
      static allocator_info do_info( std::true_type, const Allocator& alloc );
      static allocator_info do_info( std::false_type, const Allocator& alloc );
      /// \}

    };

    template<typename Allocator>
    class allocator_traits<allocator_reference<Allocator>>
      : public allocator_traits<Allocator>
    {

    };

  } // namespace memory
} // namespace bit

#include "detail/allocator_traits.inl"

#endif /* BIT_MEMORY_ALLOCATORS_ALLOCATOR_TRAITS_HPP */
<commit_msg>Fixed failing gcc build<commit_after>/**
 * \file allocator_traits.hpp
 *
 * \brief This header contains the trait type \c allocator_traits that
 *        determines or infers default values for the Allocator concept
 *
 * \author Matthew Rodusek (matthew.rodusek@gmail.com)
 */
#ifndef BIT_MEMORY_ALLOCATORS_ALLOCATOR_TRAITS_HPP
#define BIT_MEMORY_ALLOCATORS_ALLOCATOR_TRAITS_HPP

#include "concepts/Stateless.hpp"         // is_stateless
#include "concepts/Allocator.hpp"         // is_allocator, allocator_has_allocate,
#include "concepts/ExtendedAllocator.hpp" // allocator_has_extended_try_allocate

#include "allocator_reference.hpp" // allocator_reference
#include "allocator_info.hpp"      // allocator_info
#include "errors.hpp"              // get_out_of_memory_handler
#include "macros.hpp"              // BIT_MEMORY_UNUSED
#include "owner.hpp"               // owner

#include <type_traits> // std::true_type, std::false_type, etc
#include <cstddef>     // std::size_t, std::ptrdiff_t
#include <limits>      // std::numeric_limits
#include <memory>      // std::addressof

namespace bit {
  namespace memory {

    //////////////////////////////////////////////////////////////////////////
    /// \brief The allocator_traits class template provides a standardized
    ///        way to access allocator functionality
    ///
    /// This will infer defaults for any optional values that may not be
    /// present in the allocator
    ///
    /// \tparam Allocator the allocator type. Must satisfy Allocator concept
    //////////////////////////////////////////////////////////////////////////
    template<typename Allocator>
    class allocator_traits
    {

      static_assert( is_allocator<Allocator>::value, "Allocator must be an Allocator" );

      //----------------------------------------------------------------------
      // Public Members
      //----------------------------------------------------------------------
    public:

      using size_type       = allocator_size_type_t<Allocator>;
      using difference_type = allocator_difference_type_t<Allocator>;
      using pointer         = allocator_pointer_t<Allocator>;
      using const_pointer   = allocator_const_pointer_t<Allocator>;

      // qualified 'bit::memory::' to avoid '-fpermissive' errors with gcc
      using is_stateless               = bit::memory::is_stateless<Allocator>;
      using is_always_equal            = allocator_is_always_equal<Allocator>;
      using default_alignment          = allocator_default_alignment<Allocator>;
      using max_alignment              = allocator_max_alignment<Allocator>;
      using can_truncate_deallocations = allocator_can_truncate_deallocations<Allocator>;
      using knows_ownership            = allocator_knows_ownership<Allocator>;

      //----------------------------------------------------------------------
      // Allocation
      //----------------------------------------------------------------------
    public:

      /// \brief Attempts to allocate memory of at least \p size bytes,
      ///        aligned to \p align boundary
      ///
      /// On failure, this function returns \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \return the pointer to the allocated memory
      static pointer try_allocate( Allocator& alloc,
                                   size_type size,
                                   size_type align ) noexcept;

      /// \brief Attempts to allocate memory of at least \p size bytes,
      ///        aligned to \p align boundary with an offset of \p offset bytes
      ///
      /// On failure, this function returns \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \param offset the offset of the allocation
      /// \return the pointer to the allocated memory
      template<typename U = Allocator, typename = std::enable_if<is_extended_allocator<U>::value>>
      static pointer try_allocate( Allocator& alloc,
                                   size_type size,
                                   size_type align,
                                   size_type offset ) noexcept;

      /// \brief Allocates memory of at least \p size bytes, aligned to \p
      ///        align boundary
      ///
      /// On failure, this function may throw or invoke the out_of_memory
      /// handler before returning \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \return the pointer to the allocated member
      static pointer allocate( Allocator& alloc,
                               size_type size,
                               size_type align );

      /// \brief Allocates memory of at least \p size bytes, aligned to \p
      ///        align boundary
      ///
      /// On failure, this function may throw or invoke the out_of_memory
      /// handler before returning \p nullptr
      ///
      /// \param alloc the allocator to allocate from
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \param offset the offset of the allocation
      /// \return the pointer to the allocated member
      template<typename U = Allocator, typename = std::enable_if<allocator_has_extended_allocate<U>::value>>
      static pointer allocate( Allocator& alloc,
                               size_type size,
                               size_type align,
                               size_type offset );

      //----------------------------------------------------------------------
      // Deallocation
      //----------------------------------------------------------------------

      /// \brief Deallocates a pointer previously allocated with \p allocate
      ///        or try_allocate
      ///
      /// \param alloc the allocator to allocate from
      /// \param p the pointer to deallocate
      /// \param size the size of the allocation
      static void deallocate( Allocator& alloc, pointer p, size_type size );

      //----------------------------------------------------------------------
      // Observers
      //----------------------------------------------------------------------

      /// \brief Checks if the given allocator is known to own the specified
      ///        pointer \p p
      ///
      /// \note This directly invokes \c alloc.owns(p) . It is undefined
      ///       behaviour to invoke this if \c Allocator::owns is not
      ///       defined; branches should instead be taken by using
      ///       \ref knows_ownership
      ///
      /// \param alloc the allocator
      /// \param p the pointer
      static bool owns( Allocator& alloc, const_pointer p ) noexcept;

      /// \brief Gets the name of the specified allocator
      ///
      /// \note Not all allocators are nameable or have a name specified.
      ///       For these allocators, the string returned is "Unnamed"
      ///
      /// \note The lifetime of the pointer returned is unmanaged, and is NOT
      ///       the responsibility of the caller to free.
      ///
      /// \param alloc the allocator to get the name of
      /// \return the name of the allocator
      static allocator_info info( const Allocator& alloc ) noexcept;

      //----------------------------------------------------------------------
      // Capacity
      //----------------------------------------------------------------------

      /// \brief Gets the maximum size allocateable from this allocator
      ///
      /// \note the max_size does not guarantee that the size is able to be
      ///       allocated at the time the value is queried; it just means that
      ///       it is the maximum size that the allocator is capable of
      ///       allocating from an empty state.
      ///
      /// \param alloc the allocator to get the max size from
      /// \return the amount of bytes available for the largest possible
      ///         allocation
      static size_type max_size( const Allocator& alloc ) noexcept;

      /// \brief Gets the minimum size allocateable from this allocator
      ///
      /// \note The default is 1, if this function is not defined
      ///
      /// \param alloc the allocator to get the min size from
      /// \return the minimum amount of bytes able to allocated
      static size_type min_size( const Allocator& alloc ) noexcept;

      //----------------------------------------------------------------------
      // Private Implementation
      //----------------------------------------------------------------------
    private:

      /// \{
      /// \brief Performs the allocation, either by calling
      ///        \c Allocator::allocate, or by calling into
      ///        \c Allocator::try_allocate and defining a boilerplate
      ///        allocate function
      ///
      /// \param alloc the allocator to allocate
      /// \param size the size of the allocation
      /// \param align the alignment of the allocation
      /// \param offset the offset of the alignment
      /// \return the allocated pointer
      static pointer do_allocate( std::true_type, Allocator& alloc, size_type size, size_type align );
      static pointer do_allocate( std::false_type, Allocator& alloc, size_type size, size_type align );
      static pointer do_allocate( std::true_type, Allocator& alloc, size_type size, size_type align, size_type offset );
      static pointer do_allocate( std::false_type, Allocator& alloc, size_type size, size_type align, size_type offset );
      /// \}

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

      /// \{
      /// \brief Determines the max size of the allocator, either by
      ///        calling \c Allocator::max_size, or by assuming
      ///        \c std::numeric_limits<size_type>::max()
      ///
      /// \param alloc the allocator
      /// \return the max size
      static size_type do_max_size( std::true_type, const Allocator& alloc );
      static size_type do_max_size( std::false_type, const Allocator& alloc );
      /// \}

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

      /// \{
      /// \brief Determines the min size of an allocation, either by
      ///        calling \c Allocator::min_size(), or by assuming
      ///        \c 1
      ///
      /// \param alloc the allocator
      /// \return the max size
      static size_type do_min_size( std::true_type, const Allocator& alloc );
      static size_type do_min_size( std::false_type, const Allocator& alloc );
      /// \}

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

      /// \{
      /// \brief Determines the info for the allocator, either by calling
      ///        \c Allocator::info or by assuming "Unnamed" for the name
      ///
      /// \param alloc the allocator
      /// \return the name of the allocator
      static allocator_info do_info( std::true_type, const Allocator& alloc );
      static allocator_info do_info( std::false_type, const Allocator& alloc );
      /// \}

    };

    template<typename Allocator>
    class allocator_traits<allocator_reference<Allocator>>
      : public allocator_traits<Allocator>
    {

    };

  } // namespace memory
} // namespace bit

#include "detail/allocator_traits.inl"

#endif /* BIT_MEMORY_ALLOCATORS_ALLOCATOR_TRAITS_HPP */
<|endoftext|>


<commit_before>/*
 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
 *
 * File: include/eos/morphablemodel/io/cvssp.hpp
 *
 * Copyright 2014, 2015 Patrik Huber
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#pragma once

#ifndef EOS_IO_CVSSP_HPP
#define EOS_IO_CVSSP_HPP

#include "eos/morphablemodel/MorphableModel.hpp"

#include "opencv2/core/core.hpp"

#include <array>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

namespace eos {
namespace morphablemodel {

// Forward declaration
std::vector<std::array<double, 2>> load_isomap(std::string isomap_file);

/**
 * Load a shape or color model from a .scm file containing
 * a Morphable Model in the Surrey format. CVSSP's software
 * internally trains and stores the model in this custom binary
 * format and this class provides means to load them.
 *
 * Note on multi-resolution models: The landmarks to vertex-id mapping is
 * always the same. The lowest resolution model has all the landmarks defined
 * and for the higher resolutions, the mesh is divided from that on.
 * Note: For new landmarks we add, this might not be the case if we add them
 * in the highest resolution model, so take care!
 *
 * The PCA basis matrix stored in the file and loaded is the orthogonal PCA basis, i.e. it is not normalised
 * by the eigenvalues.
 *
 * @param[in] model_filename A binary .scm-file containing the model.
 * @param[in] isomap_file An optional path to an isomap containing texture coordinates.
 * @return The Morphable Model loaded from the file.
 * @throws ...
 */
inline MorphableModel load_scm_model(std::string model_filename, std::string isomap_file = std::string())
{
    using cv::Mat;
    if (sizeof(unsigned int) != 4) // note/todo: maybe use uint32 or similar instead? Yep, but still we could encounter endianness-trouble.
    {
        std::cout << "Warning: We're reading 4 Bytes from the file but sizeof(unsigned int) != 4. Check the code/behaviour." << std::endl;
    }
    if (sizeof(double) != 8)
    {
        std::cout << "Warning: We're reading 8 Bytes from the file but sizeof(double) != 8. Check the code/behaviour." << std::endl;
    }

    std::ifstream model_file(model_filename, std::ios::binary);
    if (!model_file)
    {
        const std::string msg("Unable to open model file: " + model_filename);
        std::cout << msg << std::endl;
        throw std::runtime_error(msg);
    }

    // Reading the shape model
    // Read (reference?) num triangles and vertices
    unsigned int num_vertices = 0;
    unsigned int num_triangles = 0;
    model_file.read(reinterpret_cast<char*>(&num_vertices),
                    4); // 1 char = 1 byte. uint32=4bytes. float64=8bytes.
    model_file.read(reinterpret_cast<char*>(&num_triangles), 4);

    // Read triangles
    std::vector<std::array<int, 3>> triangle_list;

    triangle_list.resize(num_triangles);
    unsigned int v0, v1, v2;
    for (unsigned int i = 0; i < num_triangles; ++i)
    {
        v0 = v1 = v2 = 0;
        model_file.read(reinterpret_cast<char*>(&v0), 4); // would be nice to pass a &vector and do it in one
        model_file.read(reinterpret_cast<char*>(&v1), 4); // go, but didn't work. Maybe a cv::Mat would work?
        model_file.read(reinterpret_cast<char*>(&v2), 4);
        triangle_list[i][0] = v0;
        triangle_list[i][1] = v1;
        triangle_list[i][2] = v2;
    }

    // Read number of rows and columns of the shape projection matrix (pcaBasis)
    unsigned int num_shape_pca_coeffs = 0;
    unsigned int num_shape_dims = 0; // dimension of the shape vector (3*num_vertices)
    model_file.read(reinterpret_cast<char*>(&num_shape_pca_coeffs), 4);
    model_file.read(reinterpret_cast<char*>(&num_shape_dims), 4);

    if (3 * num_vertices != num_shape_dims)
    {
        std::cout << "Warning: Number of shape dimensions is not equal to three times the number of "
                     "vertices. Something will probably go wrong during the loading."
                  << std::endl;
    }

    // Read shape projection matrix
    Mat orthonormal_pca_basis_shape(num_shape_dims, num_shape_pca_coeffs,
                                    CV_32FC1); // m x n (rows x cols) = numShapeDims x numShapePcaCoeffs
    std::cout << "Loading shape PCA basis matrix with " << orthonormal_pca_basis_shape.rows << " rows and "
              << orthonormal_pca_basis_shape.cols << " cols." << std::endl;
    for (unsigned int col = 0; col < num_shape_pca_coeffs; ++col)
    {
        for (unsigned int row = 0; row < num_shape_dims; ++row)
        {
            double var = 0.0;
            model_file.read(reinterpret_cast<char*>(&var), 8);
            orthonormal_pca_basis_shape.at<float>(row, col) = static_cast<float>(var);
        }
    }

    // Read mean shape vector
    unsigned int mean_dims = 0; // dimension of the mean (3*num_vertices)
    model_file.read(reinterpret_cast<char*>(&mean_dims), 4);
    if (mean_dims != num_shape_dims)
    {
        std::cout << "Warning: Number of shape dimensions is not equal to the number of dimensions of the "
                     "mean. Something will probably go wrong during the loading."
                  << std::endl;
    }
    Mat mean_shape(mean_dims, 1, CV_32FC1);
    unsigned int counter = 0;
    double vd0, vd1, vd2;
    for (unsigned int i = 0; i < mean_dims / 3; ++i)
    {
        vd0 = vd1 = vd2 = 0.0;
        model_file.read(reinterpret_cast<char*>(&vd0), 8);
        model_file.read(reinterpret_cast<char*>(&vd1), 8);
        model_file.read(reinterpret_cast<char*>(&vd2), 8);
        mean_shape.at<float>(counter, 0) = static_cast<float>(vd0);
        ++counter;
        mean_shape.at<float>(counter, 0) = static_cast<float>(vd1);
        ++counter;
        mean_shape.at<float>(counter, 0) = static_cast<float>(vd2);
        ++counter;
    }

    // Read shape eigenvalues
    unsigned int num_eigenvals_shape = 0;
    model_file.read(reinterpret_cast<char*>(&num_eigenvals_shape), 4);
    if (num_eigenvals_shape != num_shape_pca_coeffs)
    {
        std::cout << "Warning: Number of coefficients in the PCA basis matrix is not equal to the number of "
                     "eigenvalues. Something will probably go wrong during the loading."
                  << std::endl;
    }
    Mat eigenvalues_shape(num_eigenvals_shape, 1, CV_32FC1);
    for (unsigned int i = 0; i < num_eigenvals_shape; ++i)
    {
        double var = 0.0;
        model_file.read(reinterpret_cast<char*>(&var), 8);
        eigenvalues_shape.at<float>(i, 0) = static_cast<float>(var);
    }

    // Todo: We should change these to read into an Eigen matrix directly, and not into a cv::Mat first.
    using RowMajorMatrixXf = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
    const Eigen::Map<RowMajorMatrixXf> orthonormal_pca_basis_shape_(orthonormal_pca_basis_shape.ptr<float>(),
                                                                    orthonormal_pca_basis_shape.rows,
                                                                    orthonormal_pca_basis_shape.cols);
    const Eigen::Map<RowMajorMatrixXf> eigenvalues_shape_(eigenvalues_shape.ptr<float>(),
                                                          eigenvalues_shape.rows, eigenvalues_shape.cols);
    const Eigen::Map<RowMajorMatrixXf> mean_shape_(mean_shape.ptr<float>(), mean_shape.rows, mean_shape.cols);
    const PcaModel shape_model(mean_shape_, orthonormal_pca_basis_shape_, eigenvalues_shape_, triangle_list);

    // Reading the color model
    // Read number of rows and columns of projection matrix
    unsigned int num_color_pca_coeffs = 0;
    unsigned int num_color_dims = 0;
    model_file.read(reinterpret_cast<char*>(&num_color_pca_coeffs), 4);
    model_file.read(reinterpret_cast<char*>(&num_color_dims), 4);
    // Read color projection matrix
    Mat orthonormal_pca_basis_color(num_color_dims, num_color_pca_coeffs, CV_32FC1);
    std::cout << "Loading color PCA basis matrix with " << orthonormal_pca_basis_color.rows << " rows and "
              << orthonormal_pca_basis_color.cols << " cols." << std::endl;
    for (unsigned int col = 0; col < num_color_pca_coeffs; ++col)
    {
        for (unsigned int row = 0; row < num_color_dims; ++row)
        {
            double var = 0.0;
            model_file.read(reinterpret_cast<char*>(&var), 8);
            orthonormal_pca_basis_color.at<float>(row, col) = static_cast<float>(var);
        }
    }

    // Read mean color vector
    unsigned int color_mean_dims = 0; // dimension of the mean (3*num_vertices)
    model_file.read(reinterpret_cast<char*>(&color_mean_dims), 4);
    Mat mean_color(color_mean_dims, 1, CV_32FC1);
    counter = 0;
    for (unsigned int i = 0; i < color_mean_dims / 3; ++i)
    {
        vd0 = vd1 = vd2 = 0.0;
        model_file.read(reinterpret_cast<char*>(&vd0),
                        8); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB
        model_file.read(reinterpret_cast<char*>(&vd1), 8);
        model_file.read(reinterpret_cast<char*>(&vd2), 8);
        mean_color.at<float>(counter, 0) = static_cast<float>(vd0);
        ++counter;
        mean_color.at<float>(counter, 0) = static_cast<float>(vd1);
        ++counter;
        mean_color.at<float>(counter, 0) = static_cast<float>(vd2);
        ++counter;
    }

    // Read color eigenvalues
    unsigned int num_eigenvals_color = 0;
    model_file.read(reinterpret_cast<char*>(&num_eigenvals_color), 4);
    Mat eigenvalues_color(num_eigenvals_color, 1, CV_32FC1);
    for (unsigned int i = 0; i < num_eigenvals_color; ++i)
    {
        double var = 0.0;
        model_file.read(reinterpret_cast<char*>(&var), 8);
        eigenvalues_color.at<float>(i, 0) = static_cast<float>(var);
    }

    // Todo: We should change these to read into an Eigen matrix directly, and not into a cv::Mat first.
    const Eigen::Map<RowMajorMatrixXf> orthonormal_pca_basis_color_(orthonormal_pca_basis_color.ptr<float>(),
                                                                    orthonormal_pca_basis_color.rows,
                                                                    orthonormal_pca_basis_color.cols);
    const Eigen::Map<RowMajorMatrixXf> eigenvalues_color_(eigenvalues_color.ptr<float>(),
                                                          eigenvalues_color.rows, eigenvalues_color.cols);
    const Eigen::Map<RowMajorMatrixXf> mean_color_(mean_color.ptr<float>(), mean_color.rows, mean_color.cols);
    const PcaModel color_model(mean_color_, orthonormal_pca_basis_color_, eigenvalues_color_, triangle_list);

    model_file.close();

    // Load the isomap with texture coordinates if a filename has been given:
    std::vector<std::array<double, 2>> tex_coords;
    if (!isomap_file.empty())
    {
        tex_coords = load_isomap(isomap_file);
        if (shape_model.get_data_dimension() / 3.0f != tex_coords.size())
        {
            const std::string error_msg("Error, wrong number of texture coordinates. Don't have the same "
                                        "number of texcoords than the shape model has vertices.");
            std::cout << error_msg << std::endl;
            throw std::runtime_error(error_msg);
        }
    }

    return MorphableModel(shape_model, color_model, cpp17::nullopt, tex_coords);
};

/**
 * Load a set of 2D texture coordinates pre-generated by the isomap algorithm.
 * After loading, we rescale the coordinates to [0, 1] x [0, 1].
 *
 * @param[in] isomap_file Path to an isomap file containing texture coordinates.
 * @return The 2D texture coordinates for every vertex.
 * @throws ...
 */
inline std::vector<std::array<double, 2>> load_isomap(std::string isomap_file)
{
    using std::string;
    std::vector<float> x_coords, y_coords;
    string line;
    std::ifstream file(isomap_file);
    if (!file)
    {
        const string error_msg("The isomap file could not be opened. Did you specify a correct filename? " + isomap_file);
        throw std::runtime_error(error_msg);
    } else
    {
        while (getline(file, line))
        {
            std::istringstream iss(line);
            string x, y;
            iss >> x >> y;
            x_coords.push_back(std::stof(x));
            y_coords.push_back(std::stof(y));
        }
        file.close();
    }
    // Process the coordinates: Find the min/max and rescale to [0, 1] x [0, 1]
    const auto min_max_x = std::minmax_element(begin(x_coords), end(x_coords)); // min_max_x is a pair, first=min, second=max
    const auto min_max_y = std::minmax_element(begin(y_coords), end(y_coords));

    std::vector<std::array<double, 2>> tex_coords;
    const float divisor_x = *min_max_x.second - *min_max_x.first;
    const float divisor_y = *min_max_y.second - *min_max_y.first;
    for (int i = 0; i < x_coords.size(); ++i)
    {
        tex_coords.push_back(std::array<double, 2>{
            (x_coords[i] - *min_max_x.first) / divisor_x,
            1.0f - (y_coords[i] - *min_max_y.first) / divisor_y}); // We rescale to [0, 1] and at the same
                                                                   // time flip the y-coords (because in the
                                                                   // isomap, the coordinates are stored
                                                                   // upside-down).
    }

    return tex_coords;
};

} /* namespace morphablemodel */
} /* namespace eos */

#endif /* EOS_IO_CVSSP_HPP */
<commit_msg>Removed the cv::Mat dependency from io/cvssp.hpp<commit_after>/*
 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
 *
 * File: include/eos/morphablemodel/io/cvssp.hpp
 *
 * Copyright 2014, 2015 Patrik Huber
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#pragma once

#ifndef EOS_IO_CVSSP_HPP
#define EOS_IO_CVSSP_HPP

#include "eos/morphablemodel/MorphableModel.hpp"

#include "Eigen/Core"

#include <array>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

namespace eos {
namespace morphablemodel {

// Forward declaration
std::vector<std::array<double, 2>> load_isomap(std::string isomap_file);

/**
 * Load a shape or color model from a .scm file containing
 * a Morphable Model in the Surrey format. CVSSP's software
 * internally trains and stores the model in this custom binary
 * format and this class provides means to load them.
 *
 * Note on multi-resolution models: The landmarks to vertex-id mapping is
 * always the same. The lowest resolution model has all the landmarks defined
 * and for the higher resolutions, the mesh is divided from that on.
 * Note: For new landmarks we add, this might not be the case if we add them
 * in the highest resolution model, so take care!
 *
 * The PCA basis matrix stored in the file and loaded is the orthogonal PCA basis, i.e. it is not normalised
 * by the eigenvalues.
 *
 * @param[in] model_filename A binary .scm-file containing the model.
 * @param[in] isomap_file An optional path to an isomap containing texture coordinates.
 * @return The Morphable Model loaded from the file.
 * @throws ...
 */
inline MorphableModel load_scm_model(std::string model_filename, std::string isomap_file = std::string())
{
    using Eigen::MatrixXf;
    using Eigen::VectorXf;

    if (sizeof(unsigned int) != 4) // note/todo: maybe use uint32 or similar instead? Yep, but still we could encounter endianness-trouble.
    {
        std::cout << "Warning: We're reading 4 Bytes from the file but sizeof(unsigned int) != 4. Check the code/behaviour." << std::endl;
    }
    if (sizeof(double) != 8)
    {
        std::cout << "Warning: We're reading 8 Bytes from the file but sizeof(double) != 8. Check the code/behaviour." << std::endl;
    }

    std::ifstream model_file(model_filename, std::ios::binary);
    if (!model_file)
    {
        const std::string msg("Unable to open model file: " + model_filename);
        std::cout << msg << std::endl;
        throw std::runtime_error(msg);
    }

    // Reading the shape model
    // Read (reference?) num triangles and vertices
    unsigned int num_vertices = 0;
    unsigned int num_triangles = 0;
    model_file.read(reinterpret_cast<char*>(&num_vertices),
                    4); // 1 char = 1 byte. uint32=4bytes. float64=8bytes.
    model_file.read(reinterpret_cast<char*>(&num_triangles), 4);

    // Read triangles
    std::vector<std::array<int, 3>> triangle_list;

    triangle_list.resize(num_triangles);
    unsigned int v0, v1, v2;
    for (unsigned int i = 0; i < num_triangles; ++i)
    {
        v0 = v1 = v2 = 0;
        model_file.read(reinterpret_cast<char*>(&v0), 4); // would be nice to pass a &vector and do it in one
        model_file.read(reinterpret_cast<char*>(&v1), 4); // go, but didn't work. Maybe a cv::Mat would work?
        model_file.read(reinterpret_cast<char*>(&v2), 4);
        triangle_list[i][0] = v0;
        triangle_list[i][1] = v1;
        triangle_list[i][2] = v2;
    }

    // Read number of rows and columns of the shape projection matrix (pcaBasis)
    unsigned int num_shape_pca_coeffs = 0;
    unsigned int num_shape_dims = 0; // dimension of the shape vector (3*num_vertices)
    model_file.read(reinterpret_cast<char*>(&num_shape_pca_coeffs), 4);
    model_file.read(reinterpret_cast<char*>(&num_shape_dims), 4);

    if (3 * num_vertices != num_shape_dims)
    {
        std::cout << "Warning: Number of shape dimensions is not equal to three times the number of "
                     "vertices. Something will probably go wrong during the loading."
                  << std::endl;
    }

    // Read shape projection matrix
    MatrixXf orthonormal_pca_basis_shape(
        num_shape_dims, num_shape_pca_coeffs); // m x n (rows x cols) = num_shape_dims x num_shape_pca_coeffs
    std::cout << "Loading shape PCA basis matrix with " << orthonormal_pca_basis_shape.rows() << " rows and "
              << orthonormal_pca_basis_shape.cols() << " cols." << std::endl;
    for (unsigned int col = 0; col < num_shape_pca_coeffs; ++col)
    {
        for (unsigned int row = 0; row < num_shape_dims; ++row)
        {
            double var = 0.0;
            model_file.read(reinterpret_cast<char*>(&var), 8);
            orthonormal_pca_basis_shape(row, col) = static_cast<float>(var);
        }
    }

    // Read mean shape vector
    unsigned int mean_dims = 0; // dimension of the mean (3*num_vertices)
    model_file.read(reinterpret_cast<char*>(&mean_dims), 4);
    if (mean_dims != num_shape_dims)
    {
        std::cout << "Warning: Number of shape dimensions is not equal to the number of dimensions of the "
                     "mean. Something will probably go wrong during the loading."
                  << std::endl;
    }
    VectorXf mean_shape(mean_dims);
    unsigned int counter = 0;
    double vd0, vd1, vd2;
    for (unsigned int i = 0; i < mean_dims / 3; ++i)
    {
        vd0 = vd1 = vd2 = 0.0;
        model_file.read(reinterpret_cast<char*>(&vd0), 8);
        model_file.read(reinterpret_cast<char*>(&vd1), 8);
        model_file.read(reinterpret_cast<char*>(&vd2), 8);
        mean_shape(counter) = static_cast<float>(vd0);
        ++counter;
        mean_shape(counter) = static_cast<float>(vd1);
        ++counter;
        mean_shape(counter) = static_cast<float>(vd2);
        ++counter;
    }

    // Read shape eigenvalues
    unsigned int num_eigenvals_shape = 0;
    model_file.read(reinterpret_cast<char*>(&num_eigenvals_shape), 4);
    if (num_eigenvals_shape != num_shape_pca_coeffs)
    {
        std::cout << "Warning: Number of coefficients in the PCA basis matrix is not equal to the number of "
                     "eigenvalues. Something will probably go wrong during the loading."
                  << std::endl;
    }
    VectorXf eigenvalues_shape(num_eigenvals_shape);
    for (unsigned int i = 0; i < num_eigenvals_shape; ++i)
    {
        double var = 0.0;
        model_file.read(reinterpret_cast<char*>(&var), 8);
        eigenvalues_shape(i) = static_cast<float>(var);
    }

    const PcaModel shape_model(mean_shape, orthonormal_pca_basis_shape, eigenvalues_shape, triangle_list);

    // Reading the color model
    // Read number of rows and columns of projection matrix
    unsigned int num_color_pca_coeffs = 0;
    unsigned int num_color_dims = 0;
    model_file.read(reinterpret_cast<char*>(&num_color_pca_coeffs), 4);
    model_file.read(reinterpret_cast<char*>(&num_color_dims), 4);
    // Read color projection matrix
    MatrixXf orthonormal_pca_basis_color(num_color_dims, num_color_pca_coeffs);
    std::cout << "Loading color PCA basis matrix with " << orthonormal_pca_basis_color.rows() << " rows and "
              << orthonormal_pca_basis_color.cols() << " cols." << std::endl;
    for (unsigned int col = 0; col < num_color_pca_coeffs; ++col)
    {
        for (unsigned int row = 0; row < num_color_dims; ++row)
        {
            double var = 0.0;
            model_file.read(reinterpret_cast<char*>(&var), 8);
            orthonormal_pca_basis_color(row, col) = static_cast<float>(var);
        }
    }

    // Read mean color vector
    unsigned int color_mean_dims = 0; // dimension of the mean (3*num_vertices)
    model_file.read(reinterpret_cast<char*>(&color_mean_dims), 4);
    VectorXf mean_color(color_mean_dims);
    counter = 0;
    for (unsigned int i = 0; i < color_mean_dims / 3; ++i)
    {
        vd0 = vd1 = vd2 = 0.0;
        model_file.read(reinterpret_cast<char*>(&vd0),
                        8); // order in hdf5: RGB. Order in OCV: BGR. But order in vertex.color: RGB
        model_file.read(reinterpret_cast<char*>(&vd1), 8);
        model_file.read(reinterpret_cast<char*>(&vd2), 8);
        mean_color(counter) = static_cast<float>(vd0);
        ++counter;
        mean_color(counter) = static_cast<float>(vd1);
        ++counter;
        mean_color(counter) = static_cast<float>(vd2);
        ++counter;
    }

    // Read color eigenvalues
    unsigned int num_eigenvals_color = 0;
    model_file.read(reinterpret_cast<char*>(&num_eigenvals_color), 4);
    VectorXf eigenvalues_color(num_eigenvals_color);
    for (unsigned int i = 0; i < num_eigenvals_color; ++i)
    {
        double var = 0.0;
        model_file.read(reinterpret_cast<char*>(&var), 8);
        eigenvalues_color(i) = static_cast<float>(var);
    }

    const PcaModel color_model(mean_color, orthonormal_pca_basis_color, eigenvalues_color, triangle_list);

    model_file.close();

    // Load the isomap with texture coordinates if a filename has been given:
    std::vector<std::array<double, 2>> tex_coords;
    if (!isomap_file.empty())
    {
        tex_coords = load_isomap(isomap_file);
        if (shape_model.get_data_dimension() / 3.0f != tex_coords.size())
        {
            const std::string error_msg("Error, wrong number of texture coordinates. Don't have the same "
                                        "number of texcoords than the shape model has vertices.");
            std::cout << error_msg << std::endl;
            throw std::runtime_error(error_msg);
        }
    }

    return MorphableModel(shape_model, color_model, cpp17::nullopt, tex_coords);
};

/**
 * Load a set of 2D texture coordinates pre-generated by the isomap algorithm.
 * After loading, we rescale the coordinates to [0, 1] x [0, 1].
 *
 * @param[in] isomap_file Path to an isomap file containing texture coordinates.
 * @return The 2D texture coordinates for every vertex.
 * @throws ...
 */
inline std::vector<std::array<double, 2>> load_isomap(std::string isomap_file)
{
    using std::string;
    std::vector<float> x_coords, y_coords;
    string line;
    std::ifstream file(isomap_file);
    if (!file)
    {
        const string error_msg("The isomap file could not be opened. Did you specify a correct filename? " + isomap_file);
        throw std::runtime_error(error_msg);
    } else
    {
        while (getline(file, line))
        {
            std::istringstream iss(line);
            string x, y;
            iss >> x >> y;
            x_coords.push_back(std::stof(x));
            y_coords.push_back(std::stof(y));
        }
        file.close();
    }
    // Process the coordinates: Find the min/max and rescale to [0, 1] x [0, 1]
    const auto min_max_x = std::minmax_element(begin(x_coords), end(x_coords)); // min_max_x is a pair, first=min, second=max
    const auto min_max_y = std::minmax_element(begin(y_coords), end(y_coords));

    std::vector<std::array<double, 2>> tex_coords;
    const float divisor_x = *min_max_x.second - *min_max_x.first;
    const float divisor_y = *min_max_y.second - *min_max_y.first;
    for (int i = 0; i < x_coords.size(); ++i)
    {
        tex_coords.push_back(std::array<double, 2>{
            (x_coords[i] - *min_max_x.first) / divisor_x,
            1.0f - (y_coords[i] - *min_max_y.first) / divisor_y}); // We rescale to [0, 1] and at the same
                                                                   // time flip the y-coords (because in the
                                                                   // isomap, the coordinates are stored
                                                                   // upside-down).
    }

    return tex_coords;
};

} /* namespace morphablemodel */
} /* namespace eos */

#endif /* EOS_IO_CVSSP_HPP */
<|endoftext|>