Home

Resume

Blog

Teikitu


/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/*  »Project«   Teikitu Gaming System (TgS) (∂)
    »File«      TgS Effect - Emitter - Update - Particle.c
    »Author«    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
    »Version«   4.51 / »GUID« A9981407-3EC9-42AF-8B6F-8BE6DD919615                                                                                                        */
/*   -------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
/*  Copyright: © 2002-2017, Andrew Aye.  All Rights Reserved.
    This software is free for non-commercial use.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
      following conditions are met:
        Redistribution of source code must retain this copyright notice, this list of conditions and the following disclaimers.
        Redistribution in binary form must reproduce this copyright notice, this list of conditions and the following disclaimers in the documentation and other materials
          provided with the distribution.
    The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
    The intellectual property rights of the algorithms used reside with Andrew Aye.
    You may not use this software, in whole or in part, in support of any commercial product without the express written consent of the author.
    There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".                                                   */
/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */

#define T_NAME( A, ... ) A##Emitter##__VA_ARGS__
#define T_TYPE( A, ... ) A##EMITTER##__VA_ARGS__
#define T_TEXT( ... ) __VA_ARGS__ TgT("Emitter")
#define T_EFFECT_TIME_DEFAULT 0
#define T_EFFECT_NOTIFY 1

#include "TgS Effect - Effect - Type.h_inc"
#include "TgS Effect - Internal.inl"


/* == Effect ============================================================================================================================================================ */

/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Exported Functions                                                                                                                                                    */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

TgEXTN TgVOID                               tgFX__Update__Emitter_Add_Particle( P_STg2_FX__Emitter, C_TgFLOAT32, TgSINT32 );
TgEXTN TgVOID                               tgFX__Update__Emitter_Update_Particle( P_STg2_FX__Emitter, C_TgFLOAT32, TgVEC_M_F32_04 );
TgEXTN TgVOID                               tgFX__File__Particle_Patch_File_Data( P_STg2_FX_Particle__File_Data psData );




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions                                                                                                                                                  */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

static TgVOID                               tgFX__Update__Emitter_Update_Particle__Init(
                                                P_STg2_FX__Emitter, P_STg2_FX__Emitter__Particle, C_TgFLOAT32, TgVEC_M_F32_04, TgVEC_M_F32_04, TgVEC_M_F32_04 );
static TgVOID                               tgFX__Update__Emitter_Update_Particle__Update_W( P_STg2_FX__Emitter, P_STg2_FX__Emitter__Particle );
static TgBOOL                               tgFX__Update__Emitter_Update_Particle__Update(
                                                P_STg2_FX__Emitter, P_STg2_FX__Emitter__Particle, C_TgFLOAT32, TgVEC_M_F32_04 );
static TgVOID                               tgFX__Update__Emitter_Update_Particle__Update_Collision(
                                                P_STg2_FX__Emitter, P_STg2_FX__Emitter__Particle, C_TgFLOAT32, TgVEC_M_F32_04, TgVEC_M_F32_04 );




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Shared Implementation                                                                                                                                                 */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

#include "TgS Effect - Effect - Update.i_inc"




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Private Functions                                                                                                                                                     */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgFX__File__Particle_Patch_File_Data ------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgFX__File__Particle_Patch_File_Data( P_STg2_FX_Particle__File_Data psData )
{
    TgUINTPTR                           uiData_Top;

    uiData_Top = (TgUINTPTR)psData;

    psData->m_sAnim.m_sData_Offset.m_uiLVel_M += uiData_Top;
    psData->m_sAnim.m_sData_Offset.m_uiRot_M += uiData_Top;
    psData->m_sAnim.m_sData_Offset.m_uiTurbulence += uiData_Top;
    psData->m_sAnim.m_sData_Offset.m_uiSize += uiData_Top;
    psData->m_sAnim.m_sData_Offset.m_uiColour += uiData_Top;
    psData->m_sAnim.m_sData_Offset.m_uiNew_Effect_Hash += uiData_Top;
}


/* ---- tgFX__Update__Emitter_Add_Particle ------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgFX__Update__Emitter_Add_Particle( P_STg2_FX__Emitter psEmitter, C_TgFLOAT32 fDT, TgSINT32 niParticle )
{
    /* Commit the necessary memory for the particles */
    if ((psEmitter->m_sExtend.m_niParticle + niParticle) > psEmitter->m_sExtend.m_niParticle_Commit)
    {
        TgSINT32                            niParticle_Commit;
        TgSINT32                            iCommit;
        TgSINT32                            iIndex;

        niParticle_Commit = tgCM_MIN_S32( psEmitter->m_sExtend.m_niParticle + niParticle, psEmitter->m_sExtend.m_niParticle_Max );
        iCommit = (TgSINT32)(1 + ((TgSIZE)niParticle_Commit * sizeof( STg2_FX__Emitter__Particle ) / tgMM_Page_Size()));
        if (nullptr == TgCOMMIT_VIRTUAL( psEmitter->m_sExtend.m_psParticle_Memory, tgMM_Page_Size()*(TgSIZE)iCommit ))
        {
            return;
        };

        for (iIndex = psEmitter->m_sExtend.m_niParticle_Commit; iIndex + 1 < iCommit; ++iIndex)
        {
            psEmitter->m_sExtend.m_psParticle_Memory[iIndex].m_psNext = psEmitter->m_sExtend.m_psParticle_Memory + iIndex + 1;
        };
        psEmitter->m_sExtend.m_psParticle_Memory[iIndex].m_psNext = psEmitter->m_sExtend.m_psParticle_Free;
        psEmitter->m_sExtend.m_psParticle_Free = psEmitter->m_sExtend.m_psParticle_Memory + psEmitter->m_sExtend.m_niParticle_Commit;
        psEmitter->m_sExtend.m_niParticle_Commit = iCommit;
    };

    if ((psEmitter->m_sExtend.m_niParticle + niParticle) < psEmitter->m_sExtend.m_niParticle_Commit)
    {
        return; /* WTF - Should not be possible */
    };

    while (niParticle)
    {
        CP_STg2_FX_Emitter__File_Data       psFile_Data;
        CP_STg2_FX_Particle__File_Data      psFile_Data__Particle;
        P_STg2_FX__Emitter__Particle        psParticle;
        TgVEC_F32_04                        vPos_M;
        TgVEC_F32_04                        vLVel_M_Variance;
        TgVEC_F32_04                        vLVel_M;

        psFile_Data = psEmitter->m_psFile_Data;
        psFile_Data__Particle = psEmitter->m_sExtend.m_psFile_Data__Particle;
        psParticle = psEmitter->m_sExtend.m_psParticle_Free;

        if (nullptr == psEmitter->m_sExtend.m_psParticle_Free)
        {
            return; /* WTF - Should not be possible */
        };

        psEmitter->m_sExtend.m_psParticle_Free = psEmitter->m_sExtend.m_psParticle_Free->m_psNext;

        vLVel_M_Variance.m_mData = M_MAD_F32_04( M_RAND_F32_04(), psFile_Data__Particle->m_vLVel_M_Variance_RNG, psFile_Data__Particle->m_vLVel_M_Variance_MID );
        vLVel_M.m_mData = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psLVel_M, MS_SET1_F32_04( 0.0F ) );
        vLVel_M.m_mData = M_ADD_F32_04( vLVel_M_Variance.m_mData, vLVel_M.m_mData );
        vPos_M.m_mData = MS_SET1_F32_04( 0.0F );

        switch (psFile_Data->m_enEmission_Shape)
        {
            case ETgFX_EMISSION_SHAPE__POINT:
                break;

            case ETgFX_EMISSION_SHAPE__SPHERE:
            {
                TgVEC_F32_04                        vNorm_Rnd;
                TgFLOAT32                           fLength;

                vNorm_Rnd = F_RAND_F32_04();
                vNorm_Rnd = F_NORM_LEN_F32_04( &fLength, &vNorm_Rnd );
                if (fLength < KTgEPS_F32)
                    continue;
                vPos_M.m_mData = M_MUL_F32_04( vNorm_Rnd.m_mData, M_MUL_F32_04( MS_SET1_F32_04( tgCM_RAND_F32() ), psEmitter->m_sExtend.m_vEmission_Shape ) );
                break;
            };

            case ETgFX_EMISSION_SHAPE__BOX:
            {
                vPos_M.m_mData = M_SETP_F32_04( M_MUL_F32_04( M_RAND_F32_04(), psEmitter->m_sExtend.m_vEmission_Shape ) );
                break;
            };

            case ETgFX_EMISSION_SHAPE__DISK:
            {
                tgPM_SINCOS_F32( &vPos_M.m.x, &vPos_M.m.z, tgCM_RAND_F32() * KTgF_PI_F32 );
                vPos_M.m_mData = M_MUL_F32_04( vPos_M.m_mData, M_MUL_F32_04( M_RAND_F32_04(), psEmitter->m_sExtend.m_vEmission_Shape ) );
                break;
            };

            case ETgFX_EMISSION_SHAPE__CIRCLE:
            {
                tgPM_SINCOS_F32( &vPos_M.m.x, &vPos_M.m.z, tgCM_RAND_F32() * KTgF_PI_F32 );
                vPos_M.m_mData = M_MUL_F32_04( vPos_M.m_mData, psEmitter->m_sExtend.m_vEmission_Shape );
                break;
            };

            case ETgFX_EMISSION_SHAPE__VELOCITY:
            {
                TgVEC_F32_04                        vNorm_Rnd;
                TgFLOAT32                           fLength;

                vNorm_Rnd = F_RAND_F32_04();
                vNorm_Rnd = F_NORM_LEN_F32_04( &fLength, &vNorm_Rnd );
                if (fLength < KTgEPS_F32)
                    continue;
                vPos_M.m_mData = M_MUL_F32_04( vNorm_Rnd.m_mData, psEmitter->m_sExtend.m_vEmission_Shape );
                break;
            };

            default:
                TgS_NO_DEFAULT( break );
        };

        if (psFile_Data__Particle->m_enOrientation == ETgFX_PARTICLE_ORIENTATION__SCREEN_SPACE)
        {
            vPos_M.m_mData = M_RAND_F32_04();
            vPos_M.m.z = 0.0f;
            vLVel_M.m.z = 0.0f;
        };

        ++psEmitter->m_sExtend.m_niParticle;
        --niParticle;

        tgFX__Update__Emitter_Update_Particle__Init( psEmitter, psParticle, fDT, vPos_M.m_mData, vLVel_M.m_mData, vLVel_M_Variance.m_mData );
    };
}


/* ---- tgFX__Update__Emitter_Update_Particle --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgFX__Update__Emitter_Update_Particle( P_STg2_FX__Emitter psEmitter, C_TgFLOAT32 fDT, TgVEC_M_F32_04 vdT )
{
    CP_STg2_FX_Particle__File_Data      psFile_Data__Particle;
    P_STg2_FX__Emitter__Particle        psPrev_Particle;
    P_STg2_FX__Emitter__Particle        psParticle;

    psFile_Data__Particle = psEmitter->m_sExtend.m_psFile_Data__Particle;
    psPrev_Particle = nullptr;
    psParticle = psEmitter->m_sExtend.m_psParticle_Active;
    while (psParticle)
    {
        if (TgFALSE == tgFX__Update__Emitter_Update_Particle__Update( psEmitter, psParticle, fDT, vdT ))
        {
            P_STg2_FX__Emitter__Particle        psNext_Particle;

            psNext_Particle = psParticle->m_psNext;
            psParticle->m_psNext = psEmitter->m_sExtend.m_psParticle_Free;
            psEmitter->m_sExtend.m_psParticle_Free = psParticle;

            if (psPrev_Particle)
            {
                psPrev_Particle->m_psNext = psNext_Particle;
                psParticle = psNext_Particle;
            }
            else
            {
                psEmitter->m_sExtend.m_psParticle_Active = psNext_Particle;
                psParticle = psNext_Particle;
            };

            TgDIAG( psEmitter->m_sExtend.m_niParticle );
            --psEmitter->m_sExtend.m_niParticle;
        }
        else
        {
            psPrev_Particle = psParticle;
            psParticle = psParticle->m_psNext;
        };
    };
}




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions                                                                                                                                                  */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgFX__Update__Emitter_Update_Particle --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgFX__Update__Emitter_Update_Particle__Init(
    P_STg2_FX__Emitter psEmitter, P_STg2_FX__Emitter__Particle psParticle, C_TgFLOAT32 fDT, TgVEC_M_F32_04 vPos_M, TgVEC_M_F32_04 vLVel_M, TgVEC_M_F32_04 vLVel_M_V)
{
    CP_STg2_FX_Particle__File_Data      psFile_Data__Particle;
    TgVEC_M_F32_04                      vRot_M_Variance;
    TgVEC_M_F32_04                      vSize_Variance;
    TgVEC_M_F32_04                      vRVel_M_Acceleration;

    psFile_Data__Particle = psEmitter->m_sExtend.m_psFile_Data__Particle;

    vRot_M_Variance = M_MAD_F32_04( M_RAND_F32_04(), psFile_Data__Particle->m_vRot_M_Variance_RNG, psFile_Data__Particle->m_vRot_M_Variance_MID );
    vSize_Variance = M_MAD_F32_04( M_RAND_F32_04(), psFile_Data__Particle->m_vSize_Variance_RNG, psFile_Data__Particle->m_vSize_Variance_MID );
    vRVel_M_Acceleration = M_MAD_F32_04( M_RAND_F32_04(), psFile_Data__Particle->m_vRVel_M_Acceleration_RNG, psFile_Data__Particle->m_vRVel_M_Acceleration_MID );

    psParticle->m_vRVel_M_Acceleration = vRVel_M_Acceleration;
    psParticle->m_vSize_Variance = vSize_Variance;
    psParticle->m_vRot_M_Variance = vRot_M_Variance;
    psParticle->m_vLVel_M_Variance = vLVel_M_V;
    psParticle->m_vPos_Start = psEmitter->m_vPos_W;
    psParticle->m_vRot_Start = psEmitter->m_qRot_W;

    psParticle->m_qRot_M = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psRot_M, MS_SET1_F32_04( 0.0F ) );
    psParticle->m_qRot_M = M_ADD_F32_04( psParticle->m_qRot_M, psParticle->m_vRot_M_Variance );
    psParticle->m_vPos_M = vPos_M;
    psParticle->m_vLVel_M = vLVel_M;
    psParticle->m_vLive_Time = MS_SET1_F32_04( 0.0F );
    psParticle->m_vTurbulence = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psTurbulence, MS_SET1_F32_04( 0.0F ) );
    psParticle->m_vTurbulence = M_MUL_F32_04( M_RAND_F32_04(), psParticle->m_vTurbulence );
    psParticle->m_vSize = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psSize, MS_SET1_F32_04( 0.0F ) );
    psParticle->m_vSize = M_SUB_F32_04( psParticle->m_vSize, psParticle->m_vSize_Variance );
    psParticle->m_vSize = M_MAX_F32_04( psParticle->m_vSize, MS_SET1_F32_04( 0.0F ) );
    psParticle->m_vColour = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psColour, MS_SET1_F32_04( 0.0F ) );
    tgRN_M_Calc_UV_Animation(
        &psParticle->m_vUV_01_Constant, &psParticle->m_vUV_01_Scale, psEmitter->m_tiMaterial, KTgV_ZERO_F32_04.m_mData, KTgV_ZERO_F32_04.m_mData );
    psParticle->m_fLife_Time = tgCM_MAX_F32( fDT, tgCM_RAND_F32()*psFile_Data__Particle->m_fTime_RNG + psFile_Data__Particle->m_fTime_MID );

    tgFX__Update__Emitter_Update_Particle__Update_W( psEmitter, psParticle );
}


/* ---- tgFX__Update__Emitter_Update_Particle --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgFX__Update__Emitter_Update_Particle__Update_W( P_STg2_FX__Emitter psEmitter, P_STg2_FX__Emitter__Particle psParticle )
{
    TgVEC_M_F32_04                      vPos_W;
    TgVEC_M_F32_04                      vLVel_W;

    vPos_W = psParticle->m_vPos_M;
    vLVel_W = psParticle->m_vLVel_M;

    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__ROTATION_CURRENT_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        vPos_W = M_QT_TX_F32_04( vPos_W, psEmitter->m_qRot_W );
        vLVel_W = M_QT_TX_F32_04( vLVel_W, psEmitter->m_qRot_W );
    }
    else if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__ROTATION_START_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        vPos_W = M_QT_TX_F32_04( vPos_W, psParticle->m_vRot_Start );
        vLVel_W = M_QT_TX_F32_04( vLVel_W, psParticle->m_vRot_Start );
    };

    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__POSITION_CURRENT_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        vPos_W = M_ADD_F32_04( vPos_W, psEmitter->m_vPos_W );
    }
    else if (psEmitter->m_sExtend.m_psFile_Data__Particle->m_enOrientation != ETgFX_PARTICLE_ORIENTATION__SCREEN_SPACE)
    {
        vPos_W = M_ADD_F32_04( vPos_W, psParticle->m_vPos_Start );
    };

    psParticle->m_vPos_W = vPos_W;
    psParticle->m_vLVel_W = vLVel_W;
}


/* ---- tgFX__Update__Emitter_Update_Particle --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgFX_Effect__Emitter__Update__Update_Particle__Update_M( P_STg2_FX__Emitter psEmitter, P_STg2_FX__Emitter__Particle psParticle )
{
    TgVEC_M_F32_04                      vPos_M;
    TgVEC_M_F32_04                      vLVel_M;

    vPos_M = psParticle->m_vPos_W;
    vLVel_M = psParticle->m_vLVel_W;

    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__POSITION_CURRENT_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        vPos_M = M_SUB_F32_04( vPos_M, psEmitter->m_vPos_W );
    }
    else
    {
        vPos_M = M_SUB_F32_04( vPos_M, psParticle->m_vPos_Start );
    };

    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__ROTATION_CURRENT_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        vPos_M = M_QT_INV_TX_F32_04( vPos_M, psEmitter->m_qRot_W );
        vLVel_M = M_QT_INV_TX_F32_04( vLVel_M, psEmitter->m_qRot_W );
    }
    else if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__ROTATION_START_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        TgVEC_M_F32_04                      vNorm_LVel_M;
        TgVEC_M_F32_04                      vNorm_LVel_W;

        vNorm_LVel_M = M_NORM_F32_04( psParticle->m_vLVel_M );
        vNorm_LVel_W = M_NORM_F32_04( psParticle->m_vLVel_W );
        psParticle->m_vRot_Start = M_QT_VECTOR_TO_VECTOR_F32_04( vNorm_LVel_M, vNorm_LVel_W );
        vPos_M = M_QT_INV_TX_F32_04( vPos_M, psParticle->m_vRot_Start );
        vLVel_M = M_QT_INV_TX_F32_04( vLVel_M, psParticle->m_vRot_Start );
    };

    psParticle->m_vPos_M = vPos_M;
    psParticle->m_vLVel_M = vLVel_M;
}


/* ---- tgFX__Update__Emitter_Update_Particle --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgBOOL tgFX__Update__Emitter_Update_Particle__Update( P_STg2_FX__Emitter psEmitter, P_STg2_FX__Emitter__Particle psParticle, C_TgFLOAT32 fDT, TgVEC_M_F32_04 vdT )
{
    CP_STg2_FX_Particle__File_Data      psFile_Data__Particle;
    TgVEC_M_F32_04                      vL_Vel_Acceleration_W;
    TgVEC_M_F32_04                      vL_Vel_Acceleration_M;
    TgVEC_M_F32_04                      vPos_Delta_M;
    TgFLOAT32                           fAccelTerm;
    TgUINTXX                            uiEffect_Hash;
    TgVEC_M_F32_04                      vPrev_Pos_W;

    if (psParticle->m_fLife_Time < fDT)
    {
        return (TgFALSE);
    };
    psParticle->m_vLive_Time = M_ADD_F32_04( psParticle->m_vLive_Time, vdT );
    psParticle->m_fLife_Time -= fDT;

    psFile_Data__Particle = psEmitter->m_sExtend.m_psFile_Data__Particle;
    vL_Vel_Acceleration_W = psFile_Data__Particle->m_vL_Vel_Acceleration_W;
    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__EMITTER_ATTRACTOR & psEmitter->m_psFile_Data->m_uiFlags))
    {
        TgVEC_M_F32_04                      vDist_W;

        vDist_W = M_SUB_F32_04( psParticle->m_vPos_W, psEmitter->m_vPos_W );
        vL_Vel_Acceleration_W = M_MAD_F32_04( MS_SET1_F32_04( psEmitter->m_psFile_Data->m_fEmitter_Attraction ), vDist_W, vL_Vel_Acceleration_W );
    };

    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__ROTATION_CURRENT_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags))
    {
        vL_Vel_Acceleration_M = M_QT_INV_TX_F32_04( vL_Vel_Acceleration_W, psEmitter->m_qRot_W );
    }
    else if( 0 != (ETgFX_EMITTER_FLAGS__EMISSION__ROTATION_START_RELATIVE & psEmitter->m_psFile_Data->m_uiFlags) )
    {
        vL_Vel_Acceleration_M = M_QT_INV_TX_F32_04( vL_Vel_Acceleration_W, psParticle->m_vRot_Start );
    }
    else
    {
        vL_Vel_Acceleration_M = vL_Vel_Acceleration_W;
    };

    vPos_Delta_M = tgFX_Integrate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psLVel_M, vdT, psParticle->m_vLive_Time );

    psParticle->m_vPos_M = M_ADD_F32_04( psParticle->m_vPos_M, vPos_Delta_M );
    psParticle->m_qRot_M = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psRot_M, vdT );
    psParticle->m_qRot_M = M_ADD_F32_04( psParticle->m_qRot_M, psParticle->m_vRot_M_Variance );
    psParticle->m_vTurbulence = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psTurbulence, vdT );
    psParticle->m_vTurbulence = M_MUL_F32_04( M_RAND_F32_04(), psParticle->m_vTurbulence );
    psParticle->m_vSize = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psSize, vdT );
    psParticle->m_vSize = M_SUB_F32_04( psParticle->m_vSize, psParticle->m_vSize_Variance );
    psParticle->m_vSize = M_MAX_F32_04( psParticle->m_vSize, vdT );
    psParticle->m_vColour = tgFX_Evaluate_AnimData_F32_04( psFile_Data__Particle->m_sAnim.m_sParameter.m_psColour, vdT );

    vPrev_Pos_W = psParticle->m_vPos_W;
    tgFX__Update__Emitter_Update_Particle__Update_W( psEmitter, psParticle );
    fAccelTerm = 0.5F * psParticle->m_fLife_Time * psParticle->m_fLife_Time;
    psParticle->m_vPos_W = M_MAD_F32_04( MS_SET1_F32_04( fAccelTerm ), vL_Vel_Acceleration_W, psEmitter->m_vPos_W );

    uiEffect_Hash = tgFX_Evaluate_Single_AnimData_UXX( psFile_Data__Particle->m_sAnim.m_sParameter.m_psNew_Effect_Hash, psParticle->m_fLife_Time, fDT );
    if (KTgEMPTY_HASH != uiEffect_Hash)
    {
        STg2_FX_Instance                    sInstance;

        memset( &sInstance, 0, sizeof( sInstance ) );
        sInstance.m_tiTarget = KTgRN_TARGET_ID__INVALID;
        while (KTgEMPTY_HASH != uiEffect_Hash)
        {
            sInstance.m_vPos_W.m_mData = psParticle->m_vPos_W;
            sInstance.m_vNormal.m_mData = psParticle->m_vLVel_W;
            sInstance.m_vScale.m_mData = psEmitter->m_vScale;
            sInstance.m_qRot_W.m_mData = M_QT_VECTOR_TO_VECTOR_F32_04( KTgV_UNIT_Z_F32_04.m_mData, M_NORM_F32_04( psParticle->m_vLVel_W ) );
            sInstance.m_tiParent = psEmitter->m_tiClient;

            tgFX__Update__Command__Emitter_Create( uiEffect_Hash, &sInstance );
            uiEffect_Hash = tgFX_Evaluate_Single_AnimData_UXX( psFile_Data__Particle->m_sAnim.m_sParameter.m_psNew_Effect_Hash, psParticle->m_fLife_Time, fDT );
        };
    };

    if (0 != (ETgFX_EMITTER_FLAGS__EMISSION__PARTICLE_COLLIDE_MASK & psEmitter->m_psFile_Data->m_uiFlags))
    {
        tgFX__Update__Emitter_Update_Particle__Update_Collision( psEmitter, psParticle, fDT, vdT, vPrev_Pos_W );
    };

    return (TgTRUE);
}


/* ---- tgFX__Update__Emitter_Update_Particle__Update_Collision --------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgFX__Update__Emitter_Update_Particle__Update_Collision(
    P_STg2_FX__Emitter psEmitter, P_STg2_FX__Emitter__Particle psParticle, C_TgFLOAT32 UNUSED_PARAM fDT, TgVEC_M_F32_04 UNUSED_PARAM vdT, TgVEC_M_F32_04 vPrev_Pos_W )
{
    STg2_SE_CO_Request_F32_04           sRequest;
    P_STg2_CO_Contact_F32_04            psContact;
    TgVEC_M_F32_04                      vLVelW_Normal;
    TgVEC_M_F32_04                      vRestitution;
    TgVEC_M_F32_04                      vBounce;
    TgVEC_M_F32_04                      vLVel_W;
    TgVEC_M_F32_04                      vPos_Dist;
    TgVEC_M_F32_04                      vPos_Dist_Normal;
    TgVEC_M_F32_04                      vResult_Dist;
    TgVEC_M_F32_04                      vResult_Dist_Normal;
    TgVEC_M_F32_04                      vNudge;
    TgVEC_M_F32_04                      vPos_W;

    tgSE_Init_Request( &sRequest );
    tgCO_REQ_Init_Intersect_F32_04( &sRequest.m_sCollision_Request, ETgSEGMENT, ETgCO_PURPOSE__RAY_CAST );
    tgCO_REQ_Set_Include_Filter_F32_04( &sRequest.m_sCollision_Request, ETgCO_FILTER__STATIC_LOW | ETgCO_FILTER__RAG_DOLL );
    tgGM_SG_Init_F32_04( &sRequest.m_sCollision_Request.m_sPrimitive.m_sSG, (P_TgVEC_F32_04)&vPrev_Pos_W, (P_TgVEC_F32_04)&psParticle->m_vPos_W );

    if (TgFAILED(tgSE_Collide( &sRequest, ETgSE_CHANNEL__SET )))
    {
        return;
    };

    psContact = &sRequest.m_sCollision_Request.m_psResult->m_sContact;

    vLVelW_Normal = M_DOT_F32_04( psParticle->m_vLVel_W, psContact->m_vN0.m_mData );
    vRestitution = MS_SET1_F32_04( 1.0F + psEmitter->m_sExtend.m_psFile_Data__Particle->m_fCollision_Restitution );
    vBounce = M_MUL_F32_04( vRestitution, vLVelW_Normal );
    vLVel_W = M_NMS_F32_04( psContact->m_vN0.m_mData, vBounce, psParticle->m_vLVel_W );

    vPos_Dist = M_SUB_F32_04( psParticle->m_vPos_W, vPrev_Pos_W );
    vPos_Dist_Normal = M_DOT_F32_04( vPos_Dist, psContact->m_vN0.m_mData );
    vResult_Dist = M_SUB_F32_04( psContact->m_vS0.m_mData, vPrev_Pos_W );
    vResult_Dist_Normal = M_DOT_F32_04( vResult_Dist, psContact->m_vN0.m_mData );
    vNudge = M_MUL_F32_04( psContact->m_vN0.m_mData, MS_SET1_F32_04( sRequest.m_sCollision_Request.m_fNudge ) );

    psParticle->m_vLVel_W = vLVel_W;

    if (*(P_TgFLOAT32)(&vPos_Dist_Normal) > KTgEPS_F32)
    {
        TgVEC_M_F32_04                      vDist_Ratio;
        TgVEC_M_F32_04                      vTravel;

        vDist_Ratio = M_DIV_F32_04( vResult_Dist_Normal, vPos_Dist_Normal );
        vTravel = M_SUB_F32_04( MS_SET1_F32_04( 1.0F ), vDist_Ratio );
        vPos_W = M_MAD_F32_04( vLVel_W, vTravel, psContact->m_vS0.m_mData );
    }
    else
    {
        vPos_W = psContact->m_vS0.m_mData;
    };

    psParticle->m_vPos_W = M_ADD_F32_04( vPos_W, vNudge );

    tgFX_Effect__Emitter__Update__Update_Particle__Update_M( psEmitter, psParticle );

    if (KTgEMPTY_HASH != psEmitter->m_sExtend.m_psFile_Data__Particle->m_uiCollision_New_Effect_Hash)
    {
        STg2_FX_Instance                    sInstance;

        memset( &sInstance, 0, sizeof( sInstance ) );
        sInstance.m_tiTarget = KTgRN_TARGET_ID__INVALID;
        sInstance.m_vPos_W.m_mData = psParticle->m_vPos_W;
        sInstance.m_vNormal.m_mData = psContact->m_vN0.m_mData;
        sInstance.m_vScale.m_mData = psEmitter->m_vScale;
        sInstance.m_qRot_W.m_mData = M_QT_VECTOR_TO_VECTOR_F32_04( KTgV_UNIT_Z_F32_04.m_mData, M_NORM_F32_04( psParticle->m_vLVel_W ) );
        sInstance.m_tiParent = psEmitter->m_tiClient;

        tgFX__Update__Command__Emitter_Create( psEmitter->m_sExtend.m_psFile_Data__Particle->m_uiCollision_New_Effect_Hash, &sInstance );
    };
}