/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "HeadSpin.h"

#include "max.h"
#include "resource.h"
#include "plComponent.h"
#include "plComponentReg.h"
#include "MaxMain/plPlasmaRefMsgs.h"

#include "MaxMain/plMaxNode.h"
#include "MaxExport/plExportProgressBar.h"

#include "hsTypes.h"

#include "plLODFadeComponent.h"

#include "pfSurface/plFadeOpacityMod.h"
#include "pfSurface/plDistOpacityMod.h"

void DummyCodeIncludeFuncLODFade()
{
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// LODFadeComponent first, similar and related BlendOnto component further on in file.
CLASS_DESC(plLODFadeComponent, gLODFadeCompDesc, "LOD Blend",  "LODBlend", COMP_TYPE_GRAPHICS, LODFADE_COMP_CID)

ParamBlockDesc2 gLODFadeBk
(   
    plComponent::kBlkComp, _T("LODFade"), 0, &gLODFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_LODFADE, IDS_COMP_LODFADE, 0, 0, NULL,

    plLODFadeComponent::kHasBase,   _T("HasBase"),  TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_LODFADE_HASBASE,
        end,

    plLODFadeComponent::kBase, _T("Base"),  TYPE_INODE,     0, 0,
        p_ui,   TYPE_PICKNODEBUTTON, IDC_COMP_LODFADE_BASE,
        p_prompt, IDS_COMP_LODFADE_BASE,
        end,

    plLODFadeComponent::kDistance, _T("Distance"), TYPE_FLOAT,  0, 0,   
        p_default, 50.0,
        p_range, 0.0, 1000.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_LODFADE_DISTANCE, IDC_COMP_LODFADE_DISTANCE_SPIN, 1.0,
        end,    

    plLODFadeComponent::kTransition, _T("Transition"), TYPE_FLOAT,  0, 0,   
        p_default, 10.0,
        p_range, 0.0, 100.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_LODFADE_TRANSITION, IDC_COMP_LODFADE_TRANSITION_SPIN, 1.0,
        end,    

    plLODFadeComponent::kFadeBase,  _T("FadeBase"), TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_LODFADE_FADEBASE,
        p_enable_ctrls,     1, plLODFadeComponent::kBaseFirst,
        end,

    plLODFadeComponent::kBaseFirst, _T("BaseFirst"),    TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_LODFADE_BASEFIRST,
        end,


    end
);

void plLODFadeComponent::ISetToFadeBase(plMaxNode* node, plMaxNode* base, plErrorMsg* pErrMsg)
{
    if( fCompPB->GetInt(kBaseFirst) )
        node->AddRenderDependency(base);
    else
        base->AddRenderDependency(node);

    Box3 fade = base->GetFade();
    Point3 maxs = fade.Max();
    float fadeInStart = fCompPB->GetFloat(kDistance) - fCompPB->GetFloat(kTransition);
    if( fadeInStart < 0 )
        fadeInStart = 0;
    float fadeInEnd = fCompPB->GetFloat(kDistance);
    Point3 mins(fadeInStart, fadeInEnd, -1.f);
    fade = Box3(mins, maxs);
    base->SetFade(fade);

    node->SetNoDeferDraw(true);
    base->SetNoDeferDraw(true);
}

hsBool plLODFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    if( fCompPB->GetInt(kHasBase) )
    {
        plMaxNode* base = (plMaxNode*)fCompPB->GetINode(kBase, TimeValue(0));
        if( base )
        {
            if( fCompPB->GetInt(kFadeBase) )
            {
                ISetToFadeBase(node, base, pErrMsg);
            }
            else
            {
                node->AddRenderDependency(base);
                node->SetNoDeferDraw(true);
            }
        }
    }
    Box3 fade = node->GetFade();
    Point3 mins = fade.Min();
    float fadeOutStart = fCompPB->GetFloat(kDistance);
    float fadeOutEnd = fadeOutStart + fCompPB->GetFloat(kTransition);
    Point3 maxs(fadeOutEnd, fadeOutStart, 1.f);
    fade = Box3(mins, maxs);
    node->SetFade(fade);

    return true;
}

hsBool plLODFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plLODFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    return true; 
}

plLODFadeComponent::plLODFadeComponent()
{
    fClassDesc = &gLODFadeCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// BlendOnto component next.
CLASS_DESC(plBlendOntoComponent, gBlendOntoCompDesc, "Blend Onto",  "BlendOnto", COMP_TYPE_GRAPHICS, BLENDONTO_COMP_CID)

ParamBlockDesc2 gBlendOntoBk
(   
    plComponent::kBlkComp, _T("BlendOnto"), 0, &gBlendOntoCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_BLENDONTO, IDS_COMP_BLENDONTO, 0, 0, NULL,

    plBlendOntoComponent::kBaseNodes,   _T("BaseNodes"),    TYPE_INODE_TAB, 0,      P_CAN_CONVERT, 0,
        p_ui,           TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS,
        p_classID,      triObjectClassID,
        end,

    plBlendOntoComponent::kSortFaces,   _T("SortFaces"),    TYPE_BOOL, 0, 0,
        p_default,  TRUE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTO_SORTFACES,
        end,


    end
);

hsBool plBlendOntoComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    hsBool someBase = false;
    int numBase = fCompPB->Count(kBaseNodes);
    int i;
    for( i = 0; i < numBase; i++ )
    {
        plMaxNode* base = (plMaxNode*)fCompPB->GetINode(kBaseNodes, TimeValue(0), i);

        if( base )
        {
            node->AddRenderDependency(base);
            node->SetNoDeferDraw(true);
            if( !fCompPB->GetInt(kSortFaces) )
                node->SetNoFaceSort(true);

            someBase = true;
        }
    }
    if( !someBase )
    {
        node->SetBlendToFB(true);
    }

    return true;
}

hsBool plBlendOntoComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plBlendOntoComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    return true; 
}

plBlendOntoComponent::plBlendOntoComponent()
{
    fClassDesc = &gBlendOntoCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// BlendOntoAdv component next.
CLASS_DESC(plBlendOntoAdvComponent, gBlendOntoAdvCompDesc, "Blend Onto Advanced",  "BlendOntoAdv", COMP_TYPE_GRAPHICS, BLENDONTOADV_COMP_CID)

ParamBlockDesc2 gBlendOntoAdvBk
(   
    plComponent::kBlkComp, _T("BlendOntoAdv"), 0, &gBlendOntoAdvCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_BLENDONTOADV, IDS_COMP_BLENDONTOADV, 0, 0, NULL,

    plBlendOntoAdvComponent::kBaseNodes,    _T("BaseNodes"),    TYPE_INODE_TAB, 0,      P_CAN_CONVERT, 0,
        p_ui,           TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS,
        p_classID,      triObjectClassID,
        end,

    plBlendOntoAdvComponent::kSortFaces,    _T("SortFaces"),    TYPE_BOOL, 0, 0,
        p_default,  TRUE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTOADV_SORTFACES,
        end,

    plBlendOntoAdvComponent::kSortObjects,  _T("SortObjects"),  TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTOADV_SORTOBJECTS,
        end,

    plBlendOntoAdvComponent::kOntoBlending, _T("OntoBlending"), TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTOADV_ONTOBLENDING,
        end,


    end
);

hsBool plBlendOntoAdvComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    hsBool someBase = false;
    int numBase = fCompPB->Count(kBaseNodes);
    int i;
    for( i = 0; i < numBase; i++ )
    {
        plMaxNode* base = (plMaxNode*)fCompPB->GetINode(kBaseNodes, TimeValue(0), i);

        if( base )
        {
            node->AddRenderDependency(base);
            if( !fCompPB->GetInt(kOntoBlending) )
                node->SetNoDeferDraw(true);
            if( !fCompPB->GetInt(kSortFaces) )
                node->SetNoFaceSort(true);

            someBase = true;
        }
    }
    if( !someBase )
    {
        node->SetBlendToFB(true);
    }

    return true;
}

hsBool plBlendOntoAdvComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plBlendOntoAdvComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    return true; 
}

plBlendOntoAdvComponent::plBlendOntoAdvComponent()
{
    fClassDesc = &gBlendOntoAdvCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// Force drawing before the avatar.

const Class_ID B4AV_COMP_CID(0x14536d5b, 0x17dc623b);

class plB4AvComponent : public plComponent
{
public:
    plB4AvComponent();
    void DeleteThis() { delete this; }

    // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
    // of properties on the MaxNode, as it's still indeterminant.
    virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};


CLASS_DESC(plB4AvComponent, gB4AvCompDesc, "Draw B4 Avatar",  "B4Av", COMP_TYPE_GRAPHICS, B4AV_COMP_CID)

ParamBlockDesc2 gB4AvBk
(   
    plComponent::kBlkComp, _T("B4Av"), 0, &gB4AvCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_SORT_AS_OPAQUE, IDS_COMP_SORT_AS_OPAQUE, 0, 0, NULL,

    end
);

hsBool plB4AvComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    node->SetSortAsOpaque(true);
    return true;
}

hsBool plB4AvComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plB4AvComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    return true; 
}

plB4AvComponent::plB4AvComponent()
{
    fClassDesc = &gB4AvCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////
// DistFadeComponent - just feeding them a bit more rope.
CLASS_DESC(plDistFadeComponent, gDistFadeCompDesc, "Distance Fade",  "DistFade", COMP_TYPE_GRAPHICS, DISTFADE_COMP_CID)

ParamBlockDesc2 gDistFadeBk
(   
    plComponent::kBlkComp, _T("DistFade"), 0, &gDistFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_DISTFADE, IDS_COMP_DISTFADE, 0, 0, NULL,

    plDistFadeComponent::kFadeInActive, _T("FadeInActive"), TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_DISTFADE_IN_ACTIVE,
        p_enable_ctrls,     2, plDistFadeComponent::kFadeInStart, plDistFadeComponent::kFadeInEnd,
        end,

    plDistFadeComponent::kFadeInStart, _T("FadeInStart"), TYPE_FLOAT,   0, 0,   
        p_default, 5.0,
        p_range, 0.0, 1000.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_DISTFADE_INSTART, IDC_COMP_DISTFADE_INSTART_SPIN, 1.0,
        end,    

    plDistFadeComponent::kFadeInEnd, _T("FadeInEnd"), TYPE_FLOAT,   0, 0,   
        p_default, 10.0,
        p_range, 0.0, 1000.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_DISTFADE_INEND, IDC_COMP_DISTFADE_INEND_SPIN, 1.0,
        end,    

    plDistFadeComponent::kFadeOutActive,    _T("FadeOutActive"),    TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_DISTFADE_OUT_ACTIVE,
        p_enable_ctrls,     2, plDistFadeComponent::kFadeOutStart, plDistFadeComponent::kFadeOutEnd,
        end,

    plDistFadeComponent::kFadeOutStart, _T("FadeOutStart"), TYPE_FLOAT,     0, 0,   
        p_default, 50.0,
        p_range, 0.0, 1000.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_DISTFADE_OUTSTART, IDC_COMP_DISTFADE_OUTSTART_SPIN, 1.0,
        end,    

    plDistFadeComponent::kFadeOutEnd, _T("FadeOutEnd"), TYPE_FLOAT,     0, 0,   
        p_default, 100.0,
        p_range, 0.0, 1000.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_DISTFADE_OUTEND, IDC_COMP_DISTFADE_OUTEND_SPIN, 1.0,
        end,    


    end
);

hsBool plDistFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    // If we're turned off, just return.
    if( !fCompPB->GetInt(kFadeInActive) && !fCompPB->GetInt(kFadeOutActive) )
        return true;

    Box3 fade;
    Point3 mins(0.f, 0.f, 0.f);
    Point3 maxs(0.f, 0.f, 0.f);


    if( fCompPB->GetInt(kFadeInActive) )
    {
        mins[0] = fCompPB->GetFloat(kFadeInStart);
        mins[1] = fCompPB->GetFloat(kFadeInEnd);
    }
    if( fCompPB->GetInt(kFadeOutActive) )
    {
        maxs[0] = fCompPB->GetFloat(kFadeOutStart);
        maxs[1] = fCompPB->GetFloat(kFadeOutEnd);
    }

    // We're not really sure how the artist has artistically interpreted
    // the parameters. What we want is:
    //      Nearest point where object starts to fade in == mins[0]
    //      Nearest point where object is opaque == mins[1]
    //      Farthest point where object is opaque == maxs[1]
    //      Farthest point where object fades out completely = maxs[0]
    //
    // If the artist says they want it to start off opaque, fade out, then
    // fade back in once it's far away, we'll explain in person why that's stupid,
    // and in the meantime prevent them from doing that by assuming they just
    // have the parameter order flipped.
    //
    // So, they've either given us 2 distances or 2 pairs of distances.
    // If they've just given 2 distances, we use as is,
    // but if they've given 2 pairs, we have to arrange them to fit the
    // above model.
    //
    // So first thing to do is figure out how many (valid) distances we have.
    if( (mins[0] == 0) && (mins[1] == 0) && (maxs[0] == 0) && (maxs[1] == 0) )
    {
        // Okay, they gave no valid distances. Just pretend we were never here.
        return true;
    }
    if( (mins[0] == 0) && (mins[1] == 0) )
    {
        // maxs must be valid, just go with them.
        fade = IFadeFromPoint(maxs);
    }
    else if( (maxs[0] == 0) && (maxs[1] == 0) )
    {
        // mins must be valid, just go with them.
        fade = IFadeFromPoint(mins);
    }
    else
    {
        // They're both "valid". Give it a shot.
        fade = IFadeFromPair(mins, maxs);
    }

    node->SetFade(fade);

    return true;
}

void plDistFadeComponent::ISwap(float& p0, float& p1)
{
    float t = p0;
    p0 = p1;
    p1 = t;
}

Box3 plDistFadeComponent::IFadeFromPoint(Point3& mins)
{
    Point3 maxs(0.f, 0.f, 0.f);
    if( mins[0] < mins[1] )
        mins[2] = -1.f;
    else if( mins[0] > mins[1] )
        mins[2] = 1.f;
    else
        mins[2] = 0;

    return Box3(mins, maxs);
}

Box3 plDistFadeComponent::IFadeFromPair(Point3& mins, Point3& maxs)
{
    if( mins[0] > maxs[0] )
    {
        ISwap(mins[0], maxs[0]);
        ISwap(mins[1], maxs[1]);
    }
    if( mins[0] > mins[1] )
    {
        ISwap(mins[0], mins[1]);
    }
    if( maxs[0] < maxs[1] )
    {
        // Poor confused bastard, take a guess what he wants.
        ISwap(maxs[0], maxs[1]);
    }
    if( mins[0] < mins[1] )
        mins[2] = -1.f;
    else
        mins[2] = 0;
    if( maxs[0] > maxs[1] )
        maxs[2] = 1.f;
    else
        maxs[2] = 0;
    return Box3(mins, maxs);
}


hsBool plDistFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plDistFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    return true; 
}

plDistFadeComponent::plDistFadeComponent()
{
    fClassDesc = &gDistFadeCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// LOS Fade component next.

class plLOSFadeComponent : public plComponent
{
public:
    enum
    {
        kBoundsCenter,
        kFadeInTime,
        kFadeOutTime
    };

public:
    plLOSFadeComponent();
    void DeleteThis() { delete this; }

    // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
    // of properties on the MaxNode, as it's still indeterminant.
    virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};


CLASS_DESC(plLOSFadeComponent, gLOSFadeCompDesc, "LOS Fade",  "LOSFade", COMP_TYPE_GRAPHICS, LOSFADE_COMP_CID)

ParamBlockDesc2 gLOSFadeBk
(   
    plComponent::kBlkComp, _T("LOSFade"), 0, &gLOSFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_LOSFADE, IDS_COMP_LOSFADE, 0, 0, NULL,

    plLOSFadeComponent::kBoundsCenter,  _T("BoundsCenter"), TYPE_BOOL, 0, 0,
        p_default,  FALSE,
        p_ui,   TYPE_SINGLECHEKBOX, IDC_COMP_BOUNDSCENTER,
        end,

    plLOSFadeComponent::kFadeInTime, _T("kFadeInTime"), TYPE_FLOAT,     0, 0,   
        p_default, 0.5,
        p_range, 0.0, 5.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_FADEINTIME, IDC_COMP_FADEINTIME_SPIN, 1.0,
        end,    
    

    plLOSFadeComponent::kFadeOutTime, _T("kFadeOutTime"), TYPE_FLOAT,   0, 0,   
        p_default, 1.0,
        p_range, 0.0, 5.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_FADEOUTTIME, IDC_COMP_FADEOUTTIME_SPIN, 1.0,
        end,    
    

    end
);

hsBool plLOSFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    node->SetForceMatShade(true);
    node->SetForceLocal(true);
    node->SetForceMaterialCopy(true);
    return true;
}

hsBool plLOSFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plLOSFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    plFadeOpacityMod* fade = TRACKED_NEW plFadeOpacityMod;
    
    if( fCompPB->GetInt(kBoundsCenter) )
        fade->SetFlag(plFadeOpacityMod::kBoundsCenter);
    else
        fade->ClearFlag(plFadeOpacityMod::kBoundsCenter);

    fade->SetFadeUp(fCompPB->GetFloat(kFadeInTime));
    fade->SetFadeDown(fCompPB->GetFloat(kFadeOutTime));

    node->AddModifier(fade, node->GetKey()->GetName());

    return true; 
}

plLOSFadeComponent::plLOSFadeComponent()
{
    fClassDesc = &gLOSFadeCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// GZ Marker Fade component next.

const Class_ID GZFADE_COMP_CID(0x27173270, 0x4f4486f);

class plGZFadeComponent : public plComponent
{
public:
    enum
    {
        kOpaque,
        kTransp
    };

public:
    plGZFadeComponent();
    void DeleteThis() { delete this; }

    // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
    // of properties on the MaxNode, as it's still indeterminant.
    virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};


CLASS_DESC(plGZFadeComponent, gGZFadeCompDesc, "GZ Fade",  "GZFade", COMP_TYPE_GRAPHICS, GZFADE_COMP_CID)

ParamBlockDesc2 gGZFadeBk
(   
    plComponent::kBlkComp, _T("GZFade"), 0, &gGZFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_GZFADE, IDS_COMP_GZ_FADE, 0, 0, NULL,

    plGZFadeComponent::kOpaque, _T("kOpaque"), TYPE_FLOAT,  0, 0,   
        p_default, 15.0,
        p_range, 0.0, 100.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_GZ_OPAQUE, IDC_COMP_GZ_OPAQUE_SPIN, 1.0,
        end,    
    
    plGZFadeComponent::kTransp, _T("kTransp"), TYPE_FLOAT,  0, 0,   
        p_default, 20.0,
        p_range, 0.0, 100.0,
        p_ui,   TYPE_SPINNER,   EDITTYPE_POS_FLOAT, 
        IDC_COMP_GZ_TRANSP, IDC_COMP_GZ_TRANSP_SPIN, 1.0,
        end,    
    

    end
);

hsBool plGZFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    float opaq = fCompPB->GetFloat(kOpaque);
    float transp = fCompPB->GetFloat(kTransp);

    pErrMsg->Set(transp <= opaq, node->GetName(), "Distance obj goes transparent must be greater than distance it's opaque").CheckAndAsk();
    pErrMsg->Set(false);

    node->SetForceMatShade(true);
    node->SetForceLocal(true);
    node->SetForceMaterialCopy(true);
    return true;
}

hsBool plGZFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plGZFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    plDistOpacityMod* fade = TRACKED_NEW plDistOpacityMod;

    float opaq = fCompPB->GetFloat(kOpaque);
    float transp = fCompPB->GetFloat(kTransp);

    fade->SetFarDist(opaq, transp);

    node->AddModifier(fade, node->GetKey()->GetName());

    return true; 
}

plGZFadeComponent::plGZFadeComponent()
{
    fClassDesc = &gGZFadeCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////
// Force dynamic material - keeps the material colors from getting burnt into the verts.

const Class_ID DYNMAT_COMP_CID(0x2ea4671f, 0x163b12ac);

class plDynMatComponent : public plComponent
{
public:
    enum
    {
        kOpaque,
        kTransp
    };

public:
    plDynMatComponent();
    void DeleteThis() { delete this; }

    // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
    // of properties on the MaxNode, as it's still indeterminant.
    virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
    virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};


CLASS_DESC(plDynMatComponent, gDynMatCompDesc, "Force Dyn Mat",  "DynMat", COMP_TYPE_GRAPHICS, DYNMAT_COMP_CID)

ParamBlockDesc2 gDynMatBk
(   
    plComponent::kBlkComp, _T("DynMat"), 0, &gDynMatCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,

    IDD_COMP_DYNMAT, IDS_COMP_DYNMAT, 0, 0, NULL,

    end
);

hsBool plDynMatComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
{
    node->SetForceMatShade(true);
    return true;
}

hsBool plDynMatComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
{
    return true;
}

hsBool plDynMatComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) 
{ 
    return true; 
}

plDynMatComponent::plDynMatComponent()
{
    fClassDesc = &gDynMatCompDesc;
    fClassDesc->MakeAutoParamBlocks(this);
}