/*==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 "plRealTimeLightBase.h"
#include "iparamm2.h"
#include "resource.h"
#include "decomp.h"
#include "hsv.h"
#include "target.h"
#include "MaxPlasmaMtls/Layers/plLayerTex.h"
#include "MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h"


static int GetTargetPoint(TimeValue t, INode *inode, Point3& p) 
{
    Matrix3 tmat;
    if (inode->GetTargetTM(t,tmat)) 
    {
        p = tmat.GetTrans();
        return 1;
    }
    else 
        return 0;
}

///////////////////////////////////////////////////////////////////////////////
//// plBaseLightProc Functions ////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//  This class provides the base functionality for all the dialog procs for
//  the ParamBlock rollouts for each light

void    plBaseLightProc::ILoadComboBox( HWND hComboBox, const char *names[] )
{
    SendMessage(hComboBox, CB_RESETCONTENT, 0, 0);
    for (int i = 0; names[i]; i++)
        SendMessage(hComboBox, CB_ADDSTRING, 0, (LPARAM)names[i]);
    SendMessage(hComboBox, CB_SETCURSEL, 0, 0);
}

void    plBaseLightProc::IBuildLightMesh( plRTLightBase *base, float coneSize )
{
    base->BuildSpotMesh( coneSize );
    base->fMesh = base->spotMesh;
}

BOOL    plBaseLightProc::DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    IParamBlock2    *pb = map->GetParamBlock();
    plRTLightBase   *gl = (plRTLightBase *) pb->GetOwner();


    switch( msg )
    {
        case WM_INITDIALOG:
            {
                HWND DecayTypeCombo = GetDlgItem(hWnd, IDC_LIGHT_DECAY);
                HWND ShadowStateCombo   = GetDlgItem(hWnd, IDC_SHADOW_TYPE);
                
                map->Invalidate(plRTLightBase::kProjMapTexButton);
            }
            break;

        case WM_COMMAND:
            break;
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////
//// plLightTexPBAccessor Class Functions /////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

plLightTexPBAccessor    plLightTexPBAccessor::fAccessor;

void    plLightTexPBAccessor::Set( PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
    plRTLightBase* Obj = (plRTLightBase*)owner;
    IParamBlock2 *pb = Obj->fLightPB;
    
    switch (id)
    {
        case plRTLightBase::kProjMapTexButton:
            if (1) //(val.bm)->bm)
            {
                if (pb->GetMap())
                    pb->GetMap()->Invalidate(plRTLightBase::kProjMapTexButton);
                //pb->SetValue(plRTLightBase::kProjMapTexButton, Obj->fIP->GetTime(), val.bm);
                PBBitmap* ThisMap = val.bm;
                Obj->SetProjMap(&ThisMap->bi);
            break;
            }
        default:
            break;

    }
}

void    plLightTexPBAccessor::Get( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid )
{
}


////////////////////////////////////////////////////////////
//
// Mouse creation callback class for this plug-in

class RTLightMouseCallBack : public CreateMouseCallBack
{
    plRTLightBase *ob;
public:
    int proc(ViewExp *vpt, int msg, int point, int flags, IPoint2 m, Matrix3 &mat);
    void SetObj(plRTLightBase *obj) { ob = obj; }
};
static RTLightMouseCallBack gRTMouseCallback;


int RTLightMouseCallBack::proc(ViewExp *vpt, int msg, int point, int flags, IPoint2 m, Matrix3 &mat)
{
    switch (msg)
    {
    case MOUSE_POINT:
    case MOUSE_MOVE:
        switch (point)
        {
        case 0:
            mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
            break;

        case 1:
            mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
            if (msg == MOUSE_POINT)
                return 0;
            break;          
        }
        break;

    case MOUSE_ABORT:
        return CREATE_ABORT;
    }

    return CREATE_CONTINUE;
}

static RTLightMouseCallBack sRTBaseLgtCreateCB;

CreateMouseCallBack* plRTLightBase::GetCreateMouseCallBack() 
{
    sRTBaseLgtCreateCB.SetObj(this);

    return &sRTBaseLgtCreateCB;
}
/////////////////////////////////////////////////////////////////////////////

#if 0
plRTLightBase::plRTLightBase() : fIP(nil), fClassDesc(nil), fLightPB(nil)
{
    fColor = Color(0.5f, 0.5f, 1.f);
}
#endif



void plRTLightBase::SetHSVColor(TimeValue t, Point3 &hsv)
{
    DWORD rgb = HSVtoRGB ((int)(hsv[0]*255.0f), (int)(hsv[1]*255.0f), (int)(hsv[2]*255.0f));
    Point3 temp = fLightPB->GetPoint3(kLightColor, t);
    Point3 rgbf;
    rgbf[0] = temp.x / 255.0f;
    rgbf[1] = temp.y / 255.0f;
    rgbf[2] = temp.z / 255.0f;
    SetRGBColor(t, rgbf);
}


Point3 plRTLightBase::GetHSVColor(TimeValue t, Interval& b)
{
    int h, s, v;
    Point3 rgbf = GetRGBColor(t, b);
    DWORD rgb = RGB((int)(rgbf[0]*255.0f), (int)(rgbf[1]*255.0f), (int)(rgbf[2]*255.0f));
    RGBtoHSV (rgb, &h, &s, &v);
    return Point3(h/255.0f, s/255.0f, v/255.0f);


}

#define FZ (0.0f)

#define SET_QUAD(face, v0, v1, v2, v3) \
    staticMesh[RT_OMNI +1].faces[face].setVerts(v0, v1, v2); \
    staticMesh[RT_OMNI +1].faces[face].setEdgeVisFlags(1,1,0); \
    staticMesh[RT_OMNI +1].faces[face+1].setVerts(v0, v2, v3); \
    staticMesh[RT_OMNI +1].faces[face+1].setEdgeVisFlags(0,1,1);

void plRTLightBase::BuildStaticMeshes()
{
    if(!meshBuilt) {
        int nverts = 6;
        int nfaces = 8;
        // Build a leetle octahedron
        staticMesh[RT_OMNI].setNumVerts(nverts);
        staticMesh[RT_OMNI].setNumFaces(nfaces);
        float s = 8.0f;
        staticMesh[RT_OMNI].setVert(0, Point3( FZ,FZ, -s));
        staticMesh[RT_OMNI].setVert(1, Point3( s, FZ, FZ));
        staticMesh[RT_OMNI].setVert(2, Point3( FZ, s, FZ));
        staticMesh[RT_OMNI].setVert(3, Point3(-s, FZ, FZ));
        staticMesh[RT_OMNI].setVert(4, Point3( FZ,-s, FZ));
        staticMesh[RT_OMNI].setVert(5, Point3( FZ,FZ,  s));
        staticMesh[RT_OMNI].faces[0].setVerts(0,1,4);
        staticMesh[RT_OMNI].faces[1].setVerts(0,4,3);
        staticMesh[RT_OMNI].faces[2].setVerts(0,3,2);
        staticMesh[RT_OMNI].faces[3].setVerts(0,2,1);
        staticMesh[RT_OMNI].faces[4].setVerts(5,1,2);
        staticMesh[RT_OMNI].faces[5].setVerts(5,2,3);
        staticMesh[RT_OMNI].faces[6].setVerts(5,3,4);
        staticMesh[RT_OMNI].faces[7].setVerts(5,4,1);
        for (int i=0; i<nfaces; i++) {
            staticMesh[RT_OMNI].faces[i].setSmGroup(i);
            staticMesh[RT_OMNI].faces[i].setEdgeVisFlags(1,1,1);
            }
        staticMesh[RT_OMNI].buildNormals();
        staticMesh[RT_OMNI].EnableEdgeList(1);

        // Build an "arrow"
        nverts = 13;
        nfaces = 16;
        staticMesh[RT_OMNI+1].setNumVerts(nverts);
        staticMesh[RT_OMNI+1].setNumFaces(nfaces);
        s = 4.0f;
        float s4 = 16.0f;
        staticMesh[RT_OMNI+1].setVert( 0, Point3( -s,-s, FZ));
        staticMesh[RT_OMNI+1].setVert( 1, Point3(  s,-s, FZ));
        staticMesh[RT_OMNI+1].setVert( 2, Point3(  s, s, FZ));
        staticMesh[RT_OMNI+1].setVert( 3, Point3( -s, s, FZ));
        staticMesh[RT_OMNI+1].setVert( 4, Point3( -s,-s, -s4));
        staticMesh[RT_OMNI+1].setVert( 5, Point3(  s,-s, -s4));
        staticMesh[RT_OMNI+1].setVert( 6, Point3(  s, s, -s4));
        staticMesh[RT_OMNI+1].setVert( 7, Point3( -s, s, -s4));
        s *= (float)2.0;
        staticMesh[RT_OMNI+1].setVert( 8, Point3( -s,-s, -s4));
        staticMesh[RT_OMNI+1].setVert( 9, Point3(  s,-s, -s4));
        staticMesh[RT_OMNI+1].setVert(10, Point3(  s, s, -s4));
        staticMesh[RT_OMNI+1].setVert(11, Point3( -s, s, -s4));
        staticMesh[RT_OMNI+1].setVert(12, Point3( FZ,FZ, -s4-s));
        SET_QUAD( 0, 1, 0, 4, 5);
        SET_QUAD( 2, 0, 3, 7, 4);
        SET_QUAD( 4, 3, 2, 6, 7);
        SET_QUAD( 6, 2, 1, 5, 6);
        SET_QUAD( 8, 0, 1, 2, 3);
        SET_QUAD(10, 8, 9, 10, 11);
        staticMesh[RT_OMNI+1].faces[12].setVerts(8,12,9);
        staticMesh[RT_OMNI+1].faces[12].setEdgeVisFlags(1,1,1);
        staticMesh[RT_OMNI+1].faces[13].setVerts(9,12,10);
        staticMesh[RT_OMNI+1].faces[13].setEdgeVisFlags(1,1,1);
        staticMesh[RT_OMNI+1].faces[14].setVerts(10,12,11);
        staticMesh[RT_OMNI+1].faces[14].setEdgeVisFlags(1,1,1);
        staticMesh[RT_OMNI+1].faces[15].setVerts(11,12,8);
        staticMesh[RT_OMNI+1].faces[15].setEdgeVisFlags(1,1,1);
        for (int i=0; i<nfaces; i++)
            staticMesh[RT_OMNI+1].faces[i].setSmGroup(i);
        staticMesh[RT_OMNI+1].buildNormals();
        staticMesh[RT_OMNI+1].EnableEdgeList(1);

        meshBuilt = 1;
    }
}


void plRTLightBase::BuildSpotMesh(float coneSize)
{
    // build a cone
    if(coneSize < 0.0f)
        return;
    int nverts = 9;
    int nfaces = 8;
    spotMesh.setNumVerts(nverts);
    spotMesh.setNumFaces(nfaces);
    double radang = 3.1415926 * coneSize / 360.0;
    float h = 20.0f;                    // hypotenuse
    float d = -h * (float)cos(radang);  // dist from origin to cone circle
    float r = h * (float)sin(radang);   // radius of cone circle
    float s = 0.70711f * r;             // sin(45) * r
    spotMesh.setVert(0, Point3( FZ, FZ, FZ));
    spotMesh.setVert(1, Point3( -r, FZ, d));
    spotMesh.setVert(2, Point3( -s, -s, d));
    spotMesh.setVert(3, Point3( FZ, -r, d));
    spotMesh.setVert(4, Point3(  s, -s, d));
    spotMesh.setVert(5, Point3(  r, FZ, d));
    spotMesh.setVert(6, Point3(  s,  s, d));
    spotMesh.setVert(7, Point3( FZ,  r, d));
    spotMesh.setVert(8, Point3( -s,  s, d));
    spotMesh.faces[0].setVerts(0,1,2);
    spotMesh.faces[1].setVerts(0,2,3);
    spotMesh.faces[2].setVerts(0,3,4);
    spotMesh.faces[3].setVerts(0,4,5);
    spotMesh.faces[4].setVerts(0,5,6);
    spotMesh.faces[5].setVerts(0,6,7);
    spotMesh.faces[6].setVerts(0,7,8);
    spotMesh.faces[7].setVerts(0,8,1);
    for (int i=0; i<nfaces; i++) {
        spotMesh.faces[i].setSmGroup(i);
        spotMesh.faces[i].setEdgeVisFlags(1,1,1);
    }
    spotMesh.buildNormals();
    spotMesh.EnableEdgeList(1);
    //fMesh = spotMesh;
}

plRTLightBase::~plRTLightBase()
{
    DeleteAllRefsFromMe();
    fLightPB = NULL;
    if( fTex )
        delete fTex;
}

IParamBlock2* plRTLightBase::GetParamBlock2()
{
    return fLightPB; 
}

IParamBlock2* plRTLightBase::GetParamBlockByID( short id )
{
    if( id == fLightPB->ID() )
        return fLightPB; 
    else 
        return nil;
}

IParamBlock2    *plRTLightBase::GetParamBlock( int i )
{
    switch( i )
    {
        case 0: return fLightPB;
        default: return nil;
    }
}


// So our animatables will show up in the trackview
int plRTLightBase::NumSubs()
{
    return 1;
}
Animatable *plRTLightBase::SubAnim(int i)
{
    return (Animatable *)fLightPB;

    switch(i)
    {           
        /*  kRefProjMap,
        kRefShadowProjMap,
        kRefShadowType,
        kRefOmniLight,
        kRefSpotLight,
        kRefTSpotLight,
        kRefDirLight,
        kRefTDirLight
        */
        case kRefOmniLight:
        case kRefSpotLight:
        case kRefTSpotLight:
        case kRefDirLight:
        case kRefTDirLight:
        case kRefProjDirLight:
            return (Animatable*)fLightPB;
        case kRefProjMap: 
            Texmap* MyMap;
            return (Animatable*) fLightPB->GetValue(kProjMapTexButton, 0, MyMap, FOREVER);          
        case kRefShadowType: 
            return NULL;
        default: return NULL;

    }

    //return (Animatable*) fLightPB;
}

TSTR plRTLightBase::SubAnimName(int i) 
{ 
    return fLightPB->GetLocalName();
        switch(i) 
        {   
        
        case kRefOmniLight:return _T("");
        case kRefSpotLight: return TSTR(GetString(IDS_DB_FSPOT));
        case kRefTSpotLight:return _T("");
        case kRefDirLight:return _T("");
        case kRefTDirLight:return _T("");
        case kRefProjMap: return TSTR(GetString(IDS_DS_PROJMAP));           
        case kRefShadowType: return _T("");
            default: return _T("");
    
/*          case PBLOCK_REF: return TSTR(GetString(IDS_RB_PARAMETERS));
            case PROJMAP_REF: return TSTR(GetString(IDS_DS_PROJMAP));
            case SHADPROJMAP_REF: return TSTR(GetString(IDS_DS_SHADPROJMAP));
            case SHADTYPE_REF: return TSTR(GetString(IDS_DS_SHAD_GEN));
            case EMITTER_REF: 
                if ( IsCompatibleRenderer ())
                    return TSTR(GetString(IDS_EMITTER));
                else
                    return _T("");
            default: return _T("");
*/
        }
}

#if 0 
RefTargetHandle plRTSpotLight::Clone(RemapDir &remap)
{
    plRTLightBase *obj = TRACKED_NEW plRTSpotLight;

    obj->GetParamBlock2->SetValue(kLightOn, t, fLightPB->GetInt(kLightOn, t));
//  obj->fLightPB->SetValue(kLightType, t, fLightPB->GetInt(kLightType, t));
    obj->fLightPB->SetValue(kLightColor, t, fLightPB->GetInt(kLightOn, t));
    obj->fLightPB->SetValue(kCastShadows, t, fLightPB->GetInt(kLightOn, t));
    //obj->fLightPB->SetValue(kContrast, t, fLightPB->GetInt(kLightOn, t));
    //obj->fLightPB->SetValue(kDiffSoft, t, fLightPB->GetInt(kLightOn, t));
    //obj->fLightPB->SetValue(kDiffOn, t, fLightPB->GetInt(kLightOn, t));
    obj->fLightPB->SetValue(kSpec, t, fLightPB->GetInt(kLightOn, t));   
    obj->fLightPB->SetValue(kSpecularColorSwatch, t, fLightPB->GetInt(kLightOn, t));
    obj->fLightPB->SetValue(kIntensity, t, fLightPB->GetInt(kLightOn, t));
    
    if( IHasAttenuation() )
    {
        obj->fLightPB->SetValue(kUseAttenuationBool, t, fLightPB->GetInt(kLightType, t));
        obj->fLightPB->SetValue(kAttenMaxFalloffEdit, t, fLightPB->GetInt(kLightOn, t));
        obj->fLightPB->SetValue(kAttenTypeRadio, t, fLightPB->GetInt(kLightOn, t));
        obj->fLightPB->SetValue(kShowConeBool, t, fLightPB->GetInt(kLightOn, t));
        obj->fLightPB->SetValue(kHotSpot, t, fLightPB->GetInt(kLightOn, t));
        obj->fLightPB->SetValue(kAttenMaxFalloffEdit, fLightPB->GetInt(kLightType, t));
        obj->fLightPB->SetValue(kFallOff, t, fLightPB->GetInt(kLightOn, t));

    obj->fLightPB->SetValue(kUseProjectorBool, t, fLightPB->GetInt(kLightOn, t));
    obj->fLightPB->SetValue(kProjMapTexButton, t, fLightPB->GetInt(kLightOn, t));
    }
    obj->ReplaceReference(kRefSpotLight,fLightPB->Clone(remap));
    /*
        GeneralLight* newob = TRACKED_NEW GeneralLight(type);
    newob->enable = enable;
    newob->coneDisplay = coneDisplay;
    newob->useLight = useLight;
    newob->attenDisplay = attenDisplay;
    newob->useAtten = useAtten;
    newob->useAttenNear = useAttenNear;
    newob->attenNearDisplay = attenNearDisplay;
    newob->decayDisplay = decayDisplay;
    newob->shape = shape;
    newob->shadow = shadow;
    newob->shadowType = shadowType;
    newob->overshoot = overshoot;
    newob->projector = projector;
    newob->absMapBias = absMapBias;
    newob->exclList = exclList;
    newob->softenDiffuse = softenDiffuse;
    newob->affectDiffuse = affectDiffuse;
    newob->affectSpecular = affectSpecular;
    newob->ambientOnly = ambientOnly;
    newob->decayType = decayType;
    newob->atmosShadows = atmosShadows;
    newob->atmosOpacity = atmosOpacity;
    newob->atmosColAmt = atmosColAmt;
    newob->ReplaceReference(PBLOCK_REF,pblock->Clone(remap));
    if (projMap)     newob->ReplaceReference(PROJMAP_REF,projMap->Clone(remap));
    if (shadProjMap) newob->ReplaceReference(SHADPROJMAP_REF,shadProjMap->Clone(remap));
    if (shadType)    newob->ReplaceReference(SHADTYPE_REF,shadType->Clone(remap));
    if (emitter)     newob->ReplaceReference(EMITTER_REF ,emitter->Clone(remap));
    BaseClone(this, newob, remap);
    return(newob);
    */

    //plRTLightBase *obj = (plRTLightBase*) fClassDesc->Create(false);
    //obj->ReplaceReference(kRefComp, fLightPB->Clone(remap));
    return obj;
}
#endif


void plRTLightBase::FreeCaches()
{
//  fMesh.FreeAll();
}



void plRTLightBase::BoxCircle(TimeValue t, float r, float d, Box3& box, int extraPt, Matrix3 *tm) 
{
    Point3 q[3*NUM_CIRC_PTS];
    int npts;
    float asp;
    if ( 1 /*Circle Object*/) {     npts =  NUM_CIRC_PTS+extraPt;   asp = -1.0f; }
    else { npts = 4+extraPt;  asp = -1.0; } 
    GetConePoints(t, asp , r, d, q);
    box.IncludePoints(q,npts,tm);
}

void plRTLightBase::BoxDirPoints(TimeValue t, float angle, float dist, Box3 &box, Matrix3 *tm)
 {
    int npts;
    Point3 q[3*NUM_CIRC_PTS];
    npts = 1 /*Circle Object*/? GetCirXPoints(t,angle,dist,q): GetRectXPoints(t,angle,dist,q);
    box.IncludePoints(q,npts,tm);
}


void plRTLightBase::BoxPoints(TimeValue t, float angle, float dist, Box3 &box, Matrix3 *tm) 
{
    if (IsDir())
        BoxCircle(t, angle, dist, box, 0,tm);
    else 
        BoxDirPoints(t, angle, dist, box, tm);
}


void plRTLightBase::BoxLight(TimeValue t, INode *inode, Box3& box, Matrix3 *tm) 
{
    Point3 pt;
    float d;
    if (GetTargetPoint(t, inode, pt)) 
    {
        Point3 loc = inode->GetObjectTM(t).GetTrans();
        d = FLength(loc - pt) / FLength(inode->GetObjectTM(t).GetRow(2));
        box += tm? (*tm)*Point3(0.0f, 0.0f, -d): Point3(0.0f, 0.0f, -d);
    }
    else 
    {
        d = GetTDist(t);
    //  if(fLightPB->GetInt(kLightType) == RT_FREE_DIR)
    //      d = GetFallsize(t);
        if(IsSpot())
                box += tm? (*tm)*Point3(0.0f, 0.0f, -d): Point3(0.0f, 0.0f, -d);
        if(IsDir())
        {
            d = GetFallsize(t);
            box += tm? (*tm)*Point3(0.0f, 0.0f, -d): Point3(0.0f, 0.0f, -d);

        }
    }
    if( this->ClassID() == RTSPOT_LIGHT_CLASSID )
//  if  (fLightPB->GetInt(kLightType) == RT_FREE_SPOT || fLightPB->GetInt(kLightType) == RT_TARGET_SPOT)
        if((fLightPB->GetInt(kShowConeBool,t)) || (extDispFlags & EXT_DISP_ONLY_SELECTED)) 
        {
            float rad = MaxF(GetHotspot(t), GetFallsize(t));
            if (IsDir()) 
                BoxCircle(t,rad,0.0f,box,1,tm);
            BoxCircle(t,rad,d,box,1,tm);
        }
    if( this->ClassID() == RTDIR_LIGHT_CLASSID 
        || this->ClassID() == RTPDIR_LIGHT_CLASSID )
//  if  (fLightPB->GetInt(kLightType) == RT_FREE_DIR || fLightPB->GetInt(kLightType) == RT_TARGET_DIR)
        if((extDispFlags & EXT_DISP_ONLY_SELECTED)) 
        {
            float rad = MaxF(GetHotspot(t), GetFallsize(t));
            if (IsDir()) 
                BoxCircle(t,rad,0.0f,box,1,tm);
            BoxCircle(t,rad,2.82841*GetFallsize(t),box,1,tm);   //hack, hack.  Do 2root2 at corners...
        }
    BOOL dispAtten = false;
    BOOL dispAttenNear = false;
    BOOL dispDecay = false;
    if( this->ClassID() == RTOMNI_LIGHT_CLASSID || this->ClassID() == RTSPOT_LIGHT_CLASSID )
    {
        dispAtten = fLightPB->GetInt(kUseAttenuationBool,t);
        dispAttenNear = 0; //attenNearDisplay;

        dispDecay = (GetDecayType()&&(extDispFlags & EXT_DISP_ONLY_SELECTED));
    }
    if( dispAtten || dispDecay) 
    {
        if( this->ClassID() == RTOMNI_LIGHT_CLASSID )
        { 
            Point3 q[3*NUM_CIRC_PTS];
            float rad = 0;
            if (dispAtten) 
                rad = MaxF(GetAtten(t, ATTEN_START), GetAtten(t, ATTEN_END));
            if (dispDecay) rad = MaxF(rad,0.0/*GetDecayRadius(t)*/);
            GetAttenPoints(t, rad, q);
            box.IncludePoints(q,3*NUM_CIRC_PTS,tm);
        }
        if( this->ClassID() == RTSPOT_LIGHT_CLASSID )
        {
            if (dispAtten) 
            {
                BoxPoints(t, GetFallsize(t), GetAtten(t,ATTEN_END), box, tm);
                BoxPoints(t, GetFallsize(t), GetAtten(t,ATTEN_START), box, tm);
            }
            if (dispDecay) 
                BoxPoints(t, GetFallsize(t), 0.0/*GetDecayRadius(t)*/, box, tm);
        }
    }

}

int plRTLightBase::GetRectXPoints(TimeValue t, float angle, float dist, Point3 *q) 
{
    int i;
    if(dist==0.0f) dist = .00001f;
    float ang = DegToRad(angle)/2.0f;
    float da,sn,cs,x,y,z,a;
    float aspect = GetAspect(t);
    float w = dist * (float)tan(ang) * (float)sqrt((double)aspect);
    float h = w/aspect;
    float wang = (float)atan(w/dist);
    float hang = (float)atan(h/dist);
    float aw = float(atan(w/dist)*cos(hang));  // half-angle of top and bottom arcs
    float ah = float(atan(h/dist)*cos(wang));  // half-angle of left and right arcs
    int j = 0;

    // draw horizontal and vertical center lines
    da = wang/float(NUM_HALF_ARC);
    for(i = -NUM_HALF_ARC, a = -wang; i<= NUM_HALF_ARC; i++, a+=da) 
        q[j++] = Point3(dist*(float)sin(a), 0.0f, -dist*(float)cos(a));
    da = hang/float(NUM_HALF_ARC);
    for(i = -NUM_HALF_ARC, a = -hang; i<= NUM_HALF_ARC; i++, a+=da) 
        q[j++] = Point3(0.0f, dist*(float)sin(a), -dist*(float)cos(a));


    // draw top and bottom arcs
    da = aw/float(NUM_HALF_ARC);
    sn = (float)sin(hang);
    cs = (float)cos(hang);
    for (i = -NUM_HALF_ARC, a = -aw; i<= NUM_HALF_ARC; i++, a+=da) 
    {
        x =  dist*(float)sin(a); 
        z = -dist*(float)cos(a);
        q[j]             = Point3(x, z*sn, z*cs);               
        q[j+NUM_ARC_PTS] = Point3(x,-z*sn, z*cs);               
        j++;
    }
    
    j+= NUM_ARC_PTS;

    // draw left and right arcs
    da = ah/float(NUM_HALF_ARC);
    sn = (float)sin(wang);
    cs = (float)cos(wang);
    for (i = -NUM_HALF_ARC, a = -ah; i<= NUM_HALF_ARC; i++, a+=da)
    {
        y =  dist*(float)sin(a); 
        z = -dist*(float)cos(a);
        q[j]             = Point3( z*sn, y, z*cs);                  
        q[j+NUM_ARC_PTS] = Point3(-z*sn, y, z*cs);                  
        j++;
    }

    return 6*NUM_ARC_PTS;
}

int plRTLightBase::GetCirXPoints(TimeValue t, float angle, float dist, Point3 *q) 
{
    int i;
    float ang = DegToRad(angle)/2.0f;
    float da = ang/float(NUM_HALF_ARC);
    // first draw circle:
    float d = dist*(float)cos(ang);
    GetConePoints(t, -1.0f, angle, d, q);
    int j=NUM_CIRC_PTS;
    // then draw Arc X
    float a = -ang;
    for(i = -NUM_HALF_ARC; i<= NUM_HALF_ARC; i++, a+=da) 
        q[j++] = Point3(0.0f, dist*(float)sin(a), -dist*(float)cos(a));
    a = -ang;   
    for(i = -NUM_HALF_ARC; i<= NUM_HALF_ARC; i++, a+=da) 
        q[j++] = Point3(dist*(float)sin(a), 0.0f, -dist*(float)cos(a));
    return NUM_CIRC_PTS + 2*NUM_ARC_PTS;
}

void plRTLightBase::DrawX(TimeValue t, float asp, int npts, float dist, GraphicsWindow *gw, int indx) 
{
    Point3 q[3*NUM_CIRC_PTS+1];
    Point3 u[2];
    GetConePoints(t, asp, GetFallsize(t), dist, q);
    gw->polyline(npts, q,NULL, NULL, TRUE, NULL);
    u[0] = q[0]; u[1] = q[2*indx];
    gw->polyline(2, u,NULL, NULL, FALSE, NULL);
    u[0] = q[indx]; u[1] = q[3*indx];
    gw->polyline(2, u,NULL, NULL, FALSE, NULL);
}



void plRTLightBase::GetAttenPoints(TimeValue t, float rad, Point3 *q)
{
    double a;
    float sn, cs;
    for(int i = 0; i < NUM_CIRC_PTS; i++) 
    {
        a = (double)i * 2.0 * 3.1415926 / (double)NUM_CIRC_PTS;
        sn = rad * (float)sin(a);
        cs = rad * (float)cos(a);
        q[i+0*NUM_CIRC_PTS] = Point3(sn, cs, 0.0f);
        q[i+1*NUM_CIRC_PTS] = Point3(sn, 0.0f, cs);
        q[i+2*NUM_CIRC_PTS] = Point3(0.0f, sn, cs);
    }
}


// Draw warped rectangle
void plRTLightBase::DrawWarpRect(TimeValue t, GraphicsWindow *gw, float angle, float dist, Point3 *q) 
{
    GetRectXPoints(t, angle,dist,q);
    for (int i=0; i<6; i++)
        gw->polyline(NUM_ARC_PTS, q+i*NUM_ARC_PTS,NULL, NULL, FALSE, NULL);  
}

void plRTLightBase::DrawCircleX(TimeValue t, GraphicsWindow *gw, float angle, float dist, Point3 *q) 
{
    GetCirXPoints(t, angle,dist,q);
    gw->polyline(NUM_CIRC_PTS, q,NULL, NULL, TRUE, NULL);  // circle 
    gw->polyline(NUM_ARC_PTS, q+NUM_CIRC_PTS,NULL, NULL, FALSE, NULL); // vert arc
    gw->polyline(NUM_ARC_PTS, q+NUM_CIRC_PTS+NUM_ARC_PTS,NULL, NULL, FALSE, NULL);  // horiz arc
}

void plRTLightBase::DrawSphereArcs(TimeValue t, GraphicsWindow *gw, float r, Point3 *q) 
{
    GetAttenPoints(t, r, q);
    gw->polyline(NUM_CIRC_PTS, q,               NULL, NULL, TRUE, NULL);
    gw->polyline(NUM_CIRC_PTS, q+NUM_CIRC_PTS,  NULL, NULL, TRUE, NULL);
    gw->polyline(NUM_CIRC_PTS, q+2*NUM_CIRC_PTS,NULL, NULL, TRUE, NULL);
}

//

void plRTLightBase::DrawAttenCirOrRect(TimeValue t, GraphicsWindow *gw, float dist, BOOL froze, int uicol) 
{
    if (!froze) gw->setColor( LINE_COLOR, GetUIColor(uicol));
    if (IsDir()) 
    {
        int npts,indx;
        float asp;
        npts = NUM_CIRC_PTS;    asp  = -1.0f;   indx = SEG_INDEX;
        DrawX(t, asp, npts, dist, gw, indx);
    }
    else 
    {
        Point3 q[3*NUM_CIRC_PTS+1];
        if( this->ClassID() == RTOMNI_LIGHT_CLASSID )
            DrawSphereArcs(t, gw, dist, q);
        else    
            DrawCircleX(t, gw, GetFallsize(t),dist,q);
        
    }
}


int plRTLightBase::DrawAtten(TimeValue t, INode *inode, GraphicsWindow *gw)
{
    BOOL dispAtten = false;
    BOOL dispDecay = false;
    if( this->ClassID() == RTOMNI_LIGHT_CLASSID || this->ClassID() == RTSPOT_LIGHT_CLASSID )
    {
        dispAtten = (fLightPB->GetInt(kUseAttenuationBool,t) && (extDispFlags & EXT_DISP_ONLY_SELECTED));
        //BOOL dispAttenNear = (fLightPB->GetInt(kUseNearAtten) && (extDispFlags & EXT_DISP_ONLY_SELECTED))?TRUE:fLightPB->GetInt(kShowFarAttenRanges);
        dispDecay = (GetDecayType() && (extDispFlags & EXT_DISP_ONLY_SELECTED));
    }
    if (dispAtten || dispDecay) 
    {
        Matrix3 tm = inode->GetObjectTM(t);
        gw->setTransform(tm);
        BOOL froze = inode->IsFrozen() && !inode->Dependent();
        if (dispAtten) 
        {
            DrawAttenCirOrRect(t, gw, GetAtten(t,ATTEN_START), froze, COLOR_START_RANGE);
            DrawAttenCirOrRect(t, gw, GetAtten(t,ATTEN_END), froze, COLOR_END_RANGE);
        }
        
        if (dispDecay) 
        {
            DrawAttenCirOrRect(t, gw, 0.0 /*DecayRadius() Stuff here */, froze, COLOR_DECAY_RADIUS);
        }
    }
    return 0;
}

void plRTLightBase::GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box)
{
    //BuildMeshes(t);
    //box = fMesh.getBoundingBox();
//  int nv;
//  Matrix3 tm;
//  GetMat(t, node,vpt,tm);
//  Point3 loc = tm.GetTrans();
//  nv = fMesh.getNumVerts();
//  box.Init();
//  if(!(extDispFlags & EXT_DISP_ZOOM_EXT)) 
//      box.IncludePoints(fMesh.verts,nv,&tm);
//  else
//      box += loc;
//  tm = node->GetObjectTM(t);
//  BoxLight(t, node, box, &tm);
    Point3 loc = node->GetObjectTM(t).GetTrans();
    float scaleFactor = vpt->NonScalingObjectSize()*vpt->GetVPWorldWidth(loc) / 360.0f;
    box = fMesh.getBoundingBox();
    box.Scale(scaleFactor);
    BoxLight(t, node, box, NULL);

}



/*
RefTargetHandle Clone(RemapDir &remap = NoRemap())
{

    plRTLightBase* newOb = TRACKED_NEW plRTLightBase;
    newOb->fLightPB->SetValue(kLightOn, 0, this->fLightPB->GetInt(kLightOn, 0));
    newOb->fLightPB->SetValue(kLightColor, 0, this->fLightPB->GetValue(kLightColor, 0));
    newOb->fLightPB->SetValue(kLightExclude, 0, this->fLightPB->GetValue(kLightExclude, 0));
    newOb->fLightPB->SetValue(kRed, 0, this->fLightPB->GetInt(kRed, 0));
    newOb->fLightPB->SetValue(kGreen, 0, this->fLightPB->GetInt(kGreen, 0));
    newOb->fLightPB->SetValue(kBlue, 0, this->fLightPB->GetInt(kBlue, 0));
    newOb->fLightPB->SetValue(kHue, 0, this->fLightPB->GetInt(kHue, 0));
    newOb->fLightPB->SetValue(kSat, 0, this->fLightPB->GetInt(kSat, 0));
    newOb->fLightPB->SetValue(kVal, 0, this->fLightPB->GetInt(kVal, 0));
    newOb->fLightPB->SetValue(kIntensity, 0, this->fLightPB->GetFloat(kIntensity, 0));
    newOb->fLightPB->SetValue(kContrast, 0, this->fLightPB->GetFloat(kContrast, 0));
    newOb->fLightPB->SetValue(kDiffSoft, 0, this->fLightPB->GetFloat(kDiffSoft, 0));
    newOb->fLightPB->SetValue(kDiffOn, 0, this->fLightPB->GetInt(kDiffOn, 0));
    newOb->fLightPB->SetValue(kStartAttenNear, 0, this->fLightPB->GetFloat(kStartAttenNear, 0));
    newOb->fLightPB->SetValue(kAmbiOnly, 0, this->fLightPB->GetInt(kAmbiOnly, 0));
    newOb->fLightPB->SetValue(kEndAttenNear, 0, this->fLightPB->GetFloat(kEndAttenNear, 0));
    newOb->fLightPB->SetValue(kUseNearAtten, 0, this->fLightPB->GetInt(kUseNearAtten, 0));
    newOb->fLightPB->SetValue(kShowNearAttenRanges, 0, this->fLightPB->GetInt(kShowNearAttenRanges, 0));
    newOb->fLightPB->SetValue(kStartAttenFar, 0, this->fLightPB->GetFloat(kStartAttenFar, 0));
    newOb->fLightPB->SetValue(kEndAttenFar, 0, this->fLightPB->GetFloat(kEndAttenFar, 0));
    
    newOb->fLightPB->SetValue(kUseFarAtten, 0, this->fLightPB->GetInt(kUseFarAtten, 0));
    newOb->fLightPB->SetValue(kShowFarAttenRanges, 0, this->fLightPB->GetInt(kShowFarAttenRanges, 0));
    newOb->fLightPB->SetValue(kLightDecay, 0, this->fLightPB->GetInt(kLightDecay, 0));
    newOb->fLightPB->SetValue(kRed, 0, this->fLightPB->GetInt(kRed, 0));
    newOb->fLightPB->SetValue(kGreen, 0, this->fLightPB->GetInt(kGreen, 0));
    newOb->fLightPB->SetValue(kBlue, 0, this->fLightPB->GetInt(kBlue, 0));
    newOb->fLightPB->SetValue(kHue, 0, this->fLightPB->GetInt(kHue, 0));
    newOb->fLightPB->SetValue(kSat, 0, this->fLightPB->GetInt(kSat, 0));
    newOb->fLightPB->SetValue(kVal, 0, this->fLightPB->GetInt(kVal, 0));
    newOb->fLightPB->SetValue(kIntensity, 0, this->fLightPB->GetFloat(kIntensity, 0));
    newOb->fLightPB->SetValue(kContrast, 0, this->fLightPB->GetFloat(kContrast, 0));
    newOb->fLightPB->SetValue(kDiffSoft, 0, this->fLightPB->GetFloat(kDiffSoft, 0));
    newOb->fLightPB->SetValue(kDiffOn, 0, this->fLightPB->GetInt(kDiffOn, 0));
    newOb->fLightPB->SetValue(kStartAttenNear, 0, this->fLightPB->GetFloat(kStartAttenNear, 0));
    newOb->fLightPB->SetValue(kAmbiOnly, 0, this->fLightPB->GetInt(kAmbiOnly, 0));
    newOb->fLightPB->SetValue(kEndAttenNear, 0, this->fLightPB->GetFloat(kEndAttenNear, 0));
    newOb->fLightPB->SetValue(kUseNearAtten, 0, this->fLightPB->GetInt(kUseNearAtten, 0));
    newOb->fLightPB->SetValue(kShowNearAttenRanges, 0, this->fLightPB->GetInt(kShowNearAttenRanges, 0));
    newOb->fLightPB->SetValue(kStartAttenFar, 0, this->fLightPB->GetFloat(kStartAttenFar, 0));
    newOb->fLightPB->SetValue(kEndAttenFar, 0, this->fLightPB->GetFloat(kEndAttenFar, 0));

*/

/*


    kLightType,         //Inserted in v1
        kLightOn,           //Inserted in v1
        kLightColor,        //Inserted in v1
        kLightExclude,      //Inserted in v1
        kRed,               //Inserted in v1
        kGreen,             //Inserted in v1
        kBlue,              //Inserted in v1
        kHue,               //Inserted in v1
        kSat,               //Inserted in v1
        kVal,               //Inserted in v1
        kIntensity,         //Inserted in v1
        kContrast,          //Inserted in v1
        kDiffSoft,          //Inserted in v1
        kDiffOn,            //Inserted in v1
        kSpec,              //Inserted in v1
        kAmbiOnly,          //Inserted in v1
        kStartAttenNear,     //Inserted in v1
        kEndAttenNear,       //Inserted in v1
        kUseNearAtten,       //Inserted in v1
        kShowNearAttenRanges,//Inserted in v1
        kStartAttenFar,      //Inserted in v1
        kEndAttenFar,        //Inserted in v1
        kUseFarAtten,        //Inserted in v1
        kShowFarAttenRanges, //Inserted in v1
        kLightDecay,         //Inserted in v1
        kDecayEdit,          //Inserted in v1
        kShowDecay,          //Inserted in v1
        kUseProjectBool,    //Inserted in v1
        kProjMapTexButton,  //Inserted in v1
        kShowConeBool,      //Inserted in v1
        kOvershootBool,     //Inserted in v1
        kHotSpot,           //Inserted in v1
        kFallOff,           //Inserted in v1
        kLightShapeRadio,   //Inserted in v1
        kAspect,            //Inserted in v1
        kUseProjectorBool,  //Inserted in v1
        kProjMapTexButton2, //Inserted in v1
        kTargetDist         //Inserted in v1
    GeneralLight* newob = TRACKED_NEW GeneralLight(type);
    newob->enable = enable;
    newob->coneDisplay = coneDisplay;
    newob->useLight = useLight;
    newob->attenDisplay = attenDisplay;
    newob->useAtten = useAtten;
    newob->useAttenNear = useAttenNear;
    newob->attenNearDisplay = attenNearDisplay;
    newob->decayDisplay = decayDisplay;
    newob->shape = shape;
    newob->shadow = shadow;
    newob->shadowType = shadowType;
    newob->overshoot = overshoot;
    newob->projector = projector;
    newob->absMapBias = absMapBias;
    newob->exclList = exclList;
    newob->softenDiffuse = softenDiffuse;
    newob->affectDiffuse = affectDiffuse;
    newob->affectSpecular = affectSpecular;
    newob->ambientOnly = ambientOnly;
    newob->decayType = decayType;
    newob->atmosShadows = atmosShadows;
    newob->atmosOpacity = atmosOpacity;
    newob->atmosColAmt = atmosColAmt;
    newob->ReplaceReference(PBLOCK_REF,pblock->Clone(remap));
    if (projMap)     newob->ReplaceReference(PROJMAP_REF,projMap->Clone(remap));
    if (shadProjMap) newob->ReplaceReference(SHADPROJMAP_REF,shadProjMap->Clone(remap));
    if (shadType)    newob->ReplaceReference(SHADTYPE_REF,shadType->Clone(remap));
    if (emitter)     newob->ReplaceReference(EMITTER_REF ,emitter->Clone(remap));
    BaseClone(this, newob, remap);
    return(newob);
    }
*/



//}





void plRTLightBase::GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box)
{
    GetLocalBoundBox( t, node, vpt, box );
    box = box * node->GetObjectTM( t );
}

void plRTLightBase::GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel )
{
    box = fMesh.getBoundingBox(tm);
}



int plRTLightBase::Display(TimeValue t, INode *node, ViewExp *vpt, int flags)
{

    
    Matrix3 m;
//  if (!enable) 
//      return 0;
    GraphicsWindow *gw = vpt->getGW();
    GetMat(t,node,vpt,m);
    gw->setTransform(m);
    DWORD rlim = gw->getRndLimits();
    gw->setRndLimits(GW_WIREFRAME|GW_BACKCULL|(gw->getRndMode() & GW_Z_BUFFER));
    if (node->Selected())
        gw->setColor( LINE_COLOR, GetSelColor());
    else if(!node->IsFrozen() && !node->Dependent())    
    {
        if(fLightPB->GetInt(kLightOn))
            gw->setColor( LINE_COLOR, GetUIColor(COLOR_LIGHT_OBJ));
        // I un-commented this line DS 6/11/99
        else
            gw->setColor( LINE_COLOR, 0.0f, 0.0f, 0.0f);
    }
    
    fMesh.render( gw, gw->getMaterial(),
        (flags&USE_DAMAGE_RECT) ? &vpt->GetDammageRect() : NULL, COMP_ALL); 
    
    DrawConeAndLine(t, node, gw, 1);
//  DrawAtten(t, node, gw);
    gw->setRndLimits(rlim);
    return 0;
    
}



static void RemoveScaling(Matrix3 &tm) 
{
    AffineParts ap;
    decomp_affine(tm, &ap);
    tm.IdentityMatrix();
    tm.SetRotate(ap.q);
    tm.SetTrans(ap.t);
}

void plRTLightBase::GetMat(TimeValue t, INode* inode, ViewExp *vpt, Matrix3& tm) 
{
    tm = inode->GetObjectTM(t);
//  tm.NoScale();
    RemoveScaling(tm);
    float scaleFactor = vpt->NonScalingObjectSize()*vpt->GetVPWorldWidth(tm.GetTrans())/(float)360.0;
    tm.Scale(Point3(scaleFactor,scaleFactor,scaleFactor));
}


void plRTLightBase::GetConePoints(TimeValue t, float aspect, float angle, float dist, Point3 *q) 
{
    float ta = (float)tan(0.5*DegToRad(angle));   
    if(1 /*fLightPB->GetFloat(kAspect, t) <= 0.0f*/) 
    { 
        // CIRCULAR
        float rad = dist * ta;
        double a;
        if(IsDir())
            rad = angle;
        int i;
        for(i = 0; i < NUM_CIRC_PTS; i++) {
            a = (double)i * 2.0 * 3.1415926 / (double)NUM_CIRC_PTS;
            q[i] = Point3(rad*(float)sin(a), rad*(float)cos(a), -dist);
            }
        q[i] = q[0] + Point3(0.0f, 15.0f, 0.0f);
    }
    else 
    {        
        // RECTANGULAR
        float w = IsDir()? angle : dist * ta * (float)sqrt((double)aspect);
        float h = w / aspect;
        q[0] = Point3( w, h,-dist);             
        q[1] = Point3(-w, h,-dist);             
        q[2] = Point3(-w,-h,-dist);             
        q[3] = Point3( w,-h,-dist);
        q[4] = Point3( 0.0f, h+15.0f, -dist);
        q[5] = Point3( 0.0f, h, -dist);
    }
}

#define HOTCONE     0
#define FALLCONE    1

void plRTLightBase::DrawCone(TimeValue t, GraphicsWindow *gw, float dist) 
{
    Point3 q[NUM_CIRC_PTS+1], u[3];
    int dirLight = IsDir();
    int i;
    BOOL dispAtten = false; 
    BOOL dispDecay = false;
    if( this->ClassID() == RTOMNI_LIGHT_CLASSID || this->ClassID() == RTSPOT_LIGHT_CLASSID )
    {
        dispAtten = (fLightPB->GetInt(kUseAttenuationBool, t) && (extDispFlags & EXT_DISP_ONLY_SELECTED));//attenDisplay;
        dispDecay = (/*fLightPB->GetInt(kAttenTypeRadio, t)*/GetDecayType()  && (extDispFlags & EXT_DISP_ONLY_SELECTED));
    }
    if(!IsDir())
        GetConePoints(t, -1.0f, GetHotspot(t), dist, q);
    else
        GetConePoints(t, -1.0f, 20.0, dist, q);

    gw->setColor( LINE_COLOR, GetUIColor(COLOR_HOTSPOT));
    if(1 /*Circular Hack*/) {  
        // CIRCULAR
        if(GetHotspot(t) >= GetFallsize(t)) 
        {
            // draw (far) hotspot circle
            u[0] = q[0];
            u[1] = q[NUM_CIRC_PTS];
            gw->polyline( 2, u, NULL, NULL, FALSE, NULL);
        }
        gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL);
        if (dirLight) 
        {
            // draw 4 axial hotspot lines
            for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) 
            {
                u[0] =  q[i];   u[1] =  q[i]; u[1].z += dist;
                gw->polyline( 2, u, NULL, NULL, FALSE, NULL );  
            }
            GetConePoints(t, -1.0f, 20/*GetHotspot(t)*/, 0.0f, q);
            // draw (near) hotspot circle
            gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL);
        }
        else  
        {
            // draw 4 axial lines
            u[0] = Point3(0,0,0);
            for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) 
            {
                u[1] =  q[i];
                gw->polyline( 2, u, NULL, NULL, FALSE, NULL );  
            }
        }
        if(!IsDir())
            GetConePoints(t, -1.0f, GetFallsize(t), dist, q);
        else
            GetConePoints(t, -1.0f, 200.0, dist, q);
        gw->setColor( LINE_COLOR, GetUIColor(COLOR_FALLOFF));
        if(GetHotspot(t) < GetFallsize(t)) 
        {
            // draw (far) fallsize circle
            u[0] = q[0];    u[1] = q[NUM_CIRC_PTS];
            gw->polyline( 2, u, NULL, NULL, FALSE, NULL);
            u[0] = Point3(0,0,0);
        }
        gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL);
        if (dirLight)
        {
            float dfar = q[0].z;
            float dnear = 0.0f;
            if (dispAtten)
            {
                dfar  = MinF(-GetAtten(t,ATTEN_END),dfar);
            /// dnear = MaxF(-GetAtten(t,ATTEN_START),dnear);
            }
            if (dispDecay) {
                dfar  = MinF(/*-GetDecayRadius(t)*/ 0.0,dfar);
            }

            // draw axial fallsize lines
            for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) 
            {
                u[0] =  q[i];  u[0].z = dfar;   u[1] =  q[i]; u[1].z = dnear;
                gw->polyline( 2, u, NULL, NULL, FALSE, NULL );  
            }

            GetConePoints(t, -1.0f, 10000.0, 0.0f, q);
            // draw (near) fallsize circle
            gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL);
            
        }
        else 
        {
            float cs = (float)cos(DegToRad(GetFallsize(t)*0.5f));
            float dfar = q[0].z;
            if (dispAtten)
                dfar  = MinF(-cs*GetAtten(t,ATTEN_END),dfar);
            if (dispDecay) 
                dfar  = MinF(/*-cs*GetDecayRadius(t)*/0.0,dfar);

            for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) 
            {
                u[1] =  -q[i]*dfar/dist;    
                gw->polyline( 2, u, NULL, NULL, FALSE, NULL );  
            }
        }
    }

}


int plRTLightBase::DrawConeAndLine(TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ) 
    {
    if(!IsSpot() && !IsDir())
        return 0;
    Matrix3 tm = inode->GetObjectTM(t);
    gw->setTransform(tm);
    gw->clearHitCode();
    if( 0 )
    {
        Point3 pt,v[3];
        if (GetTargetPoint(t, inode, pt)) 
        {
            float den = FLength(tm.GetRow(2));
            float dist = (den!=0) ? FLength(tm.GetTrans()-pt) / den : 0.0f;
            fLightPB->SetValue(kAttenMaxFalloffEdit, t, dist);

            //fLightPB->SetValue(kTargetDist, t, dist);
            //if (hSpotLight&&(currentEditLight==this)) {
            //  TCHAR buf[40];
            //  _stprintf(buf,_T("%0.3f"),targDist);
            //  SetWindowText(GetDlgItem(hSpotLight,IDC_TARG_DISTANCE),buf);
            //  }

            if ((drawing != -1) && (fLightPB->GetInt(kShowConeBool, t) || (extDispFlags & EXT_DISP_ONLY_SELECTED)))
                DrawCone(t, gw, dist);
            if(!inode->IsFrozen() && !inode->Dependent())
                gw->setColor( LINE_COLOR, GetUIColor(COLOR_TARGET_LINE));
            v[0] = Point3(0,0,0);
            v[1] = Point3(0.0f, 0.0f, (drawing == -1)? (-0.9f * dist): -dist);
            gw->polyline( 2, v, NULL, NULL, FALSE, NULL );  
        }
        
    }
    else if( this->ClassID() == RTSPOT_LIGHT_CLASSID )
    {
        if ((drawing != -1) && (fLightPB->GetInt(kShowConeBool, t) || (extDispFlags & EXT_DISP_ONLY_SELECTED)))
            DrawCone(t, gw, fLightPB->GetFloat( kAttenMaxFalloffEdit, t ) );
    }
    else if( this->ClassID() == RTDIR_LIGHT_CLASSID )
    {
        if ((extDispFlags & EXT_DISP_ONLY_SELECTED))
            DrawCone(t, gw, 500/*GetTDist(t)*/);
    }
    return gw->checkHitCode();
}


//// DrawArrow ///////////////////////////////////////////////////////////////

void    plRTLightBase::DrawArrow( TimeValue t, GraphicsWindow *gw, Point3 &direction, float dist ) 
{
    Point3  pts[ 5 ];

    
    pts[ 0 ] = Point3( 0, 0, 0 );
    pts[ 1 ] = direction * dist;
    pts[ 3 ] = pts[ 1 ] - direction * 10.f;
    pts[ 2 ] = pts[ 3 ] + Point3( direction.y, direction.z, direction.x ) * 5.f;

    gw->polyline( 4, pts, nil, nil, true, nil );
}

int plRTLightBase::HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt)
{
    DWORD savedLimits;
    GraphicsWindow *gw = vpt->getGW();
    HitRegion hitRegion;
    int res;
    Matrix3 m;
    MakeHitRegion(hitRegion, type, crossing, 4, p);
    Material *mtl = gw->getMaterial();
    
    gw->setRndLimits( ((savedLimits = gw->getRndLimits()) | GW_PICK) & ~(GW_ILLUM|GW_BACKCULL));
    GetMat(t,node,vpt,m);
    
    //BuildMeshes(t);
    gw->setTransform(m);
    res = fMesh.select( gw, mtl, &hitRegion, flags & HIT_ABORTONHIT);
    // if not, check the target line, and set the pair flag if it's hit
    if( !res )  
    {
        // this special case only works with point selection of targeted lights
        if((type != HITTYPE_POINT) || !node->GetTarget())
            return 0;
        // don't let line be active if only looking at selected stuff and target isn't selected
        if((flags & HIT_SELONLY) && !node->GetTarget()->Selected() )
            return 0;
        gw->clearHitCode();
        if(res = DrawConeAndLine(t, node, gw, -1))
            node->SetTargetNodePair(1);
    }
    gw->setRndLimits(savedLimits);
    return res;

    //return fMesh.select(gw,node->Mtls(),&hitRegion,flags & HIT_ABORTONHIT,node->NumMtls());
}
static void GenericSnap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt) 
{
    // Make sure the vertex priority is active and at least as important as the best snap so far
    if(snap->vertPriority > 0 && snap->vertPriority <= snap->priority) 
    {
        Matrix3 tm = inode->GetObjectTM(t); 
        GraphicsWindow *gw = vpt->getGW();  
    
        gw->setTransform(tm);

        Point2 fp = Point2((float)p->x, (float)p->y);
        IPoint3 screen3;
        Point2 screen2;
        Point3 vert(0.0f,0.0f,0.0f);

        gw->wTransPoint(&vert,&screen3);

        screen2.x = (float)screen3.x;
        screen2.y = (float)screen3.y;

        // Are we within the snap radius?
        int len = (int)Length(screen2 - fp);
        if(len <= snap->strength) 
        {
            // Is this priority better than the best so far?
            if(snap->vertPriority < snap->priority) 
            {
                snap->priority = snap->vertPriority;
                snap->bestWorld = vert * tm;
                snap->bestScreen = screen2;
                snap->bestDist = len;
            }
            // Closer than the best of this priority?
            else if(len < snap->bestDist) 
            {
                snap->priority = snap->vertPriority;
                snap->bestWorld = vert * tm;
                snap->bestScreen = screen2;
                snap->bestDist = len;
            }
        }
    }
}

void plRTLightBase::Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt) 
{
    GenericSnap(t,inode,snap,p,vpt);
}
// This generic snap routine is used to snap to the 0,0,0 point of the given node.  For lights,
// this works to snap all types.



RefTargetHandle plRTLightBase::GetReference(int i)
{
    /*
        kRefProjMap,
        kRefShadowProjMap,
        kRefShadowType,
  *///Texmap*   MyMap;
    switch(i)
        {
        case kRefGeneralLightProp:
            return NULL;
        case kRefProjMap:
            //if(fLightPB->GetTexmap(kProjMapTexButton, 0) != NULL)
            //{
            //  MyMap = fLightPB->GetTexmap(kProjMapTexButton, 0);          
            //return (RefTargetHandle) MyMap; 
            //}else
                return NULL;
        case kRefShadowProjMap:
            return NULL;
        case kRefShadowType:
            return NULL;
        case kRefOmniLight:
        case kRefSpotLight:
        case kRefTSpotLight:
        case kRefDirLight:
        case kRefTDirLight:
        case kRefProjDirLight:
            return (RefTargetHandle)fLightPB;
        default:
            return NULL;
        }

}       

void plRTLightBase::SetReference(int ref, RefTargetHandle rtarg)
{
        //Texmap* MyMap;
        switch(ref)
        {
        case kRefGeneralLightProp:
            return;
        case kRefProjMap:
                
                //MyMap = (Texmap *)rtarg;
                //fLightPB->SetValue(kProjMapTexButton, 0, MyMap);
                
                //if (projMapName) 
                //  projMapName->SetText(projMap?projMap->GetFullName().data():GetString(IDS_DB_NONE));
                return;
            
        case kRefShadowProjMap:
            return;
        case kRefShadowType:
            return;
        case kRefOmniLight:
        case kRefSpotLight:
        case kRefTSpotLight:
        case kRefDirLight:
        case kRefTDirLight:
        case kRefProjDirLight:
            fLightPB = (IParamBlock2*)rtarg; break;
        }   
        //fLightPB = (IParamBlock2*)rtarg;
}

RefResult plRTLightBase::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message)
{           
    if( fLightPB )
    {
        ParamID     param = fLightPB->LastNotifyParamID();

        if( param == kAmbientOnlyStub )
        {
            fLightPB->EnableNotifications( false );
            fLightPB->SetValue( kAmbientOnlyStub, TimeValue( 0 ), false );
            // Assume this was true, since, well, we're IN NotifyRefChanged....
            fLightPB->EnableNotifications( true );

            return REF_SUCCEED;
        }
    }

    return REF_SUCCEED;
}

void plRTLightBase::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev)
{
    fIP = ip;
    fClassDesc->BeginEditParams(ip, this, flags, prev);
}

void plRTLightBase::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next)
{   
    GenLight::EndEditParams( ip, flags, next );
    fIP = NULL;
    fClassDesc->EndEditParams(ip, this, flags, next);
}

RefResult plRTLightBase::EvalLightState(TimeValue t, Interval& valid, LightState *ls)
{
        //t uselight;
//#if 0 //fLightPB->GetInt(kLightOn, t);
    if(fLightPB->GetInt(kLightOn, t) == true)
        ls->color = GetRGBColor(t,valid);
    else
        ls->color = Color(0,0,0);
    ls->on = fLightPB->GetInt(kLightOn, t);
    ls->intens = GetIntensity(t, valid);
    if( this->ClassID() == RTSPOT_LIGHT_CLASSID || this->ClassID() == RTOMNI_LIGHT_CLASSID )
    {
        ls->hotsize = GetHotspot(t, valid);
        ls->fallsize = GetFallsize(t, valid);
        ls->useNearAtten = GetUseAttenNear();
        ls->useAtten = GetUseAtten();
        ls->attenStart = GetAtten(t, ATTEN_START, valid);
        ls->attenEnd = GetAtten(t, ATTEN_END, valid);
    }else
    {
        ls->hotsize = 20;
        ls->fallsize = 10000;
        ls->useNearAtten = false; //GetUseAttenNear();
        ls->useAtten = false; //GetUseAtten();
        //ls->attenStart = GetAtten(t, ATTEN_START, valid);
        //ls->attenEnd = GetAtten(t, ATTEN_END, valid);


    }
    ls->shape = GW_SHAPE_CIRCULAR;  //fLightPB->GetInt(kLightShapeRadio);

    ls->aspect = -1.0;//GetAspect(t, valid);
    ls->overshoot = false;  //GetOvershoot();
    ls->shadow = GetShadow();
    ls->ambientOnly = false; //fLightPB->GetValue(  kAmbiOnly,t, ls->ambientOnly, valid);   //ls->ambientOnly = fLightP.AmbiOnly;   
    ls->affectDiffuse = true;   //fLightPB->GetInt(kDiffOn,t);  //ls->affectDiffusey = fLightP.AmbiOnly;    
    ls->affectSpecular = fLightPB->GetInt(kSpec,t); //ls- = fLightP.DiffOn;;

    //ls->

    if( this->ClassID() == RTOMNI_LIGHT_CLASSID )
        ls->type = OMNI_LGT;
    else if( this->ClassID() == RTDIR_LIGHT_CLASSID || this->ClassID() == RTPDIR_LIGHT_CLASSID )
        ls->type = DIRECT_LGT;
    else
        ls->type = SPOT_LGT;

    return REF_SUCCEED;
}


#if 0
#define PLASMAOBJ_DATA_CHUNK 1

IOResult plRTLightBase::Save(ISave* isave)
{
    return IO_OK;
}

IOResult plRTLightBase::Load(ILoad* iload)
{
    ULONG nb;
    IOResult res;
    int numrefs = 0;

    while (IO_OK == (res = iload->OpenChunk()))
    {
        if (iload->CurChunkID() == PLASMAOBJ_DATA_CHUNK)
            res = iload->Read(&numrefs, sizeof(int), &nb);
        iload->CloseChunk();

        if (res != IO_OK)
            return res;
    }

    return IO_OK;
}

#endif

    
ObjLightDesc *plRTLightBase::CreateLightDesc(INode *n, BOOL forceShadowBuf)
{

    return NULL;

}


void plRTLightBase::SetHotspot(TimeValue t, float f)
{
    if(!IsSpot())
        return;
    if(f < 0.5f)
        f = 0.5f;
    if(!IsDir() && (f > 179.5f))
        f = 179.5f;
    //pblock->SetValue( PB_HOTSIZE, t, f );
    fLightPB->SetValue(kHotSpot, t, f);

    //fLightP.HotSpot = f;
    NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
}

float plRTLightBase::GetHotspot(TimeValue t, Interval& valid)
{
    Interval iv;

    if(!IsSpot() && !IsDir())
        return -1.0f;
    float f;
    //pblock->GetValue( PB_HOTSIZE, t, f, valid );
    if(IsSpot())
        fLightPB->GetValue(kHotSpot, t, f, FOREVER);
    else
        f = 20.0;

    if(GetFallsize(t, iv) < f )
        return GetFallsize(t, iv) - 2.0;
    return f;
}

void plRTLightBase::SetRGBColor(TimeValue t, Point3& rgb) 
{
    //fLightPB->SetValue(plRTLightBase::kRed, t, rgb.x);
    //fLightPB->SetValue(plRTLightBase::kGreen, t, rgb.y);
    //fLightPB->SetValue(plRTLightBase::kBlue, t, rgb.z);
    fLightPB->SetValue(kLightColor, t, rgb );
    NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);


}

Point3 plRTLightBase::GetRGBColor(TimeValue t, Interval &valid)
{   
    //Point3 Foo = Point3(fLightPB->GetInt(plRTLightBase::kRed, t), fLightPB->GetInt(plRTLightBase::kGreen, t), fLightPB->GetInt(plRTLightBase::kBlue, t));
    Point3 rgb;
    fLightPB->GetValue( kLightColor, t, rgb, valid );
    return rgb;
}



void plRTLightBase::SetFallsize(TimeValue t, float f)
{
    if(!IsSpot() )
        return;
    if(f < 0.5f)
        f = 0.5f;
    if(!IsDir() && (f > 179.5f))
        f = 179.5f;
    //pblock->SetValue( PB_FALLSIZE, t, f );
    fLightPB->SetValue(kFallOff, t, f);

    NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
}

float plRTLightBase::GetFallsize(TimeValue t, Interval& valid)
{
    if(!IsSpot() && !IsDir())
        return -1.0f;
    float f;
    
    //pblock->GetValue( PB_FALLSIZE, t, f, valid );
    if(IsSpot())
        fLightPB->GetValue(kFallOff, t, f, valid);
    else
        f = 200.0;
    return f;
}


void plRTLightBase::SetAtten(TimeValue t, int which, float f)
{

    if(which != ATTEN_START)
        fLightPB->SetValue(kAttenMaxFalloffEdit, t, f);

    NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
}   


float plRTLightBase::GetAtten(TimeValue t, int which, Interval& valid)
{   
    float f = 0.0;
    //if(fLightPB->GetInt(kEndAttenFar, t) == 1)
    //if(LyteType == RT_OMNI)
    //  fLightPB->GetValue(kStartAttenFar, t, f, FOREVER); 
    //else
    //  fLightPB->GetValue(kStartAttenNear, t, f, FOREVER); 
    if(which == LIGHT_ATTEN_START)
        return f;
    else
        return( fLightPB->GetFloat(kAttenMaxFalloffEdit, t));

    //return f;
}

    
void plRTLightBase::SetTDist(TimeValue t, float f) 
{
//  int i;
//  fLightPB->GetInt(kLightType, t, i);
    
    //pblock->SetValue( PB_TDIST, t, f );
    //To be implemented.
    if( IHasAttenuation() )
        fLightPB->SetValue(kAttenMaxFalloffEdit, t, f);
    //fLightPB->SetValue(kTargetDist, t, f);

    NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
}
    
float plRTLightBase::GetTDist(TimeValue t, Interval& valid)
{
    if(!IsSpot())
        return -1.0f;
//  int i;
    //fLightPB->GetValue(kLightType, t, i, valid);
    float f = -1.0;

    fLightPB->GetFloat( kAttenMaxFalloffEdit, t );

    //pblock->GetValue( PB_TDIST, t, f, valid );
    //fLightPB->GetValue(kTargetDist, t, f, valid);
    return f;
}

void plRTLightBase::SetConeDisplay(int s, int notify) 
{
    if(!IsDir())
        fLightPB->SetValue(kShowConeBool, 0, s);
    if(notify && IsSpot())
        NotifyDependents(FOREVER, PART_OBJ, REFMSG_CHANGE);
}

BOOL plRTLightBase::GetConeDisplay(void)
{
    if(!IsDir())
        return fLightPB->GetInt(kShowConeBool);
    return
        false;
}


void plRTLightBase::SetProjMap(BitmapInfo* pmap)
{
    //plLayerTex* MyMap = TRACKED_NEW plLayerTex;
    if(!fTex)
        fTex = TRACKED_NEW plLayerTex;
    fTex->SetBitmap(pmap);
    ReplaceReference(kRefProjMap,fTex);

    IParamBlock2 *bitmapPB = fTex->GetParamBlockByID(plLayerTex::kBlkBitmap);
    bitmapPB->SetValue(kBmpUseBitmap, 0, 1);

    // This is set in the call to fTex->SetBitmap(pmap)
    //PBBitmap pbb(*pmap);
    //bitmapPB->SetValue(kBmpBitmap, 0, &pbb);

    //Texmap* MyMap;
    //fLightPB->GetValue(kProjMapTexButton, 0, MyMap, FOREVER);
    if (fTex) fLightPB->SetValue(kUseProjectorBool, 0, true);   
    NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
    if( fLightPB->GetMap() )
    {
        fLightPB->GetMap()->Invalidate(kProjMapTexButton);
        fLightPB->GetMap()->Invalidate(kUseProjectorBool);
    }

}


BOOL plRTLightBase::GetShadow()
{
    return fLightPB->GetInt(kCastShadows);
}

void plRTLightBase::SetShadow(int a)
{
    fLightPB->SetValue(kCastShadows, 0, 1);
}

Texmap* plRTLightBase::GetProjMap() 
{ 
    if( !fLightPB->GetInt(kUseProjectorBool) )
        return NULL;

    if( GetTex() )
    {
        const char* dbgTexName = GetTex()->GetName();

        IParamBlock2 *bitmapPB = fTex->GetParamBlockByID(plLayerTex::kBlkBitmap);
        hsAssert(bitmapPB, "LayerTex with no param block");

        int noCompress = fLightPB->GetInt(kProjNoCompress);
        int noMip = fLightPB->GetInt(kProjNoMip);
        bitmapPB->SetValue(kBmpNonCompressed, TimeValue(0), noCompress);
        bitmapPB->SetValue(kBmpNoFilter, TimeValue(0), noMip);
    }

    return (Texmap*) GetTex();
}
    
void plRTLightBase::UpdateTargDistance(TimeValue t, INode* inode)
{
    if( this->ClassID() == RTSPOT_LIGHT_CLASSID )
    {
        Point3 pt,v[3];
        if (GetTargetPoint(t, inode, pt))
        {
            Matrix3 tm = inode->GetObjectTM(t);
            float den = FLength(tm.GetRow(2));
            float dist = (den!=0) ? FLength(tm.GetTrans()-pt) / den : 0.0f;
            fLightPB->SetValue(kAttenMaxFalloffEdit, t, dist);

    //      fLightPB->SetValue(kTargetDist, t, dist);
            //TCHAR buf[40];
            //_stprintf(buf,_T("%0.3f"),targDist);
            //SetWindowText(GetDlgItem(hSpotLight,IDC_TARG_DISTANCE),buf);
        }
    }

}




///  
//
//
//  Target Creation for Targeted Lights...
//
//
//


#if 0
class TSpotCreationManager : public MouseCallBack, ReferenceMaker 
{
private:
    CreateMouseCallBack *createCB;  
    INode *lgtNode,*targNode;
    plRTLightBase *lgtObject;
    TargetObject *targObject;
    int attachedToNode;
    IObjCreate *createInterface;
    ClassDesc *cDesc;
    Matrix3 mat;  // the nodes TM relative to the CP
    IPoint2 pt0;
    int ignoreSelectionChange;
    int lastPutCount;

    void CreateNewObject(); 

    int NumRefs() { return 1; }
    RefTargetHandle GetReference(int i) { return (RefTargetHandle)lgtNode; } 
    void SetReference(int i, RefTargetHandle rtarg) { lgtNode = (INode *)rtarg; }

    // StdNotifyRefChanged calls this, which can change the partID to new value 
    // If it doesnt depend on the particular message& partID, it should return
    // REF_DONTCARE
    RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, 
            PartID& partID,  RefMessage message);

public:
    void Begin( IObjCreate *ioc, ClassDesc *desc );
    void End();
        
    TSpotCreationManager()  { ignoreSelectionChange = FALSE; }
    int proc( HWND hwnd, int msg, int point, int flag, IPoint2 m );
    BOOL SupportAutoGrid(){return TRUE;}
};


#define CID_TSPOTCREATE CID_USER + 3

class TSpotCreateMode : public CommandMode {
    TSpotCreationManager proc;
public:
    void Begin( IObjCreate *ioc, ClassDesc *desc ) { proc.Begin( ioc, desc ); }
    void End() { proc.End(); }

    int Class() { return CREATE_COMMAND; }
    int ID() { return CID_TSPOTCREATE; }
    MouseCallBack *MouseProc(int *numPoints) { *numPoints = 1000000; return &proc; }
    ChangeForegroundCallback *ChangeFGProc() { return CHANGE_FG_SELECTED; }
    BOOL ChangeFG( CommandMode *oldMode ) { return (oldMode->ChangeFGProc() != CHANGE_FG_SELECTED); }
    void EnterMode() {
        }
    void ExitMode() {
        }
    BOOL IsSticky() { return FALSE; }
};

static TSpotCreateMode theTSpotCreateMode;

#endif

#if 0
void TSpotCreationManager::Begin( IObjCreate *ioc, ClassDesc *desc )
{
    createInterface = ioc;
    cDesc           = desc;
    attachedToNode  = FALSE;
    createCB        = NULL;
    lgtNode         = NULL;
    targNode        = NULL;
    lgtObject       = NULL;
    targObject      = NULL;
    CreateNewObject();
}

void TSpotCreationManager::End()
{
    if ( lgtObject ) {
        lgtObject->EndEditParams( (IObjParam*)createInterface, END_EDIT_REMOVEUI, NULL);
        if ( !attachedToNode ) {
            // RB 4-9-96: Normally the hold isn't holding when this 
            // happens, but it can be in certain situations (like a track view paste)
            // Things get confused if it ends up with undo...
            theHold.Suspend(); 
            //delete lgtObject;
            lgtObject->DeleteThis();  // JBW 11.1.99, this allows scripted plugin lights to delete cleanly
            lgtObject = NULL;
            theHold.Resume();
            // RB 7/28/97: If something has been put on the undo stack since this object was created, we have to flush the undo stack.
            if (theHold.GetGlobalPutCount()!=lastPutCount) {
                GetSystemSetting(SYSSET_CLEAR_UNDO);
                }
            macroRec->Cancel();  // JBW 4/23/99
        } 
        else if ( lgtNode ) {
             // Get rid of the reference.
            DeleteReference(0);  // sets lgtNode = NULL
        }
    }   
}

RefResult TSpotCreationManager::NotifyRefChanged(
    Interval changeInt, 
    RefTargetHandle hTarget, 
    PartID& partID,  
    RefMessage message) 
{
    switch (message) {
        
    case REFMSG_PRENOTIFY_PASTE:
    case REFMSG_TARGET_SELECTIONCHANGE:
        if ( ignoreSelectionChange ) {
            break;
        }
        if (lgtObject && lgtNode==hTarget) {
            // this will set camNode== NULL;
            DeleteReference(0);
            goto endEdit;
        }
        // fall through

    case REFMSG_TARGET_DELETED:     
        if ( lgtObject && lgtNode==hTarget ) {
            endEdit:
            lgtObject->EndEditParams( (IObjParam*)createInterface, 0, NULL);
            lgtObject  = NULL;              
            lgtNode    = NULL;
            CreateNewObject();  
            attachedToNode = FALSE;
        }
        else if (targNode==hTarget) {
            targNode = NULL;
            targObject = NULL;
        }
        break;      
    }
    return REF_SUCCEED;
}


void TSpotCreationManager::CreateNewObject()
{
    lgtObject = (plRTLightBase *)cDesc->Create();
    lastPutCount = theHold.GetGlobalPutCount();

    macroRec->BeginCreate(cDesc);  // JBW 4/23/99
    
    // Start the edit params process
    if ( lgtObject ) {
        lgtObject->BeginEditParams( (IObjParam*)createInterface, BEGIN_EDIT_CREATE, NULL );
    }   
}

static void whoa(){};

static BOOL needToss;
            
int TSpotCreationManager::proc( 
                HWND hwnd,
                int msg,
                int point,
                int flag,
                IPoint2 m )
{   
    int res;
    TSTR targName;  
    ViewExp *vpx = createInterface->GetViewport(hwnd); 
    assert( vpx );

    switch ( msg ) {
    case MOUSE_POINT:
        switch ( point ) {
        case 0:
            pt0 = m;
            assert( lgtObject );                    
            vpx->CommitImplicitGrid(m, flag); //KENNY MERGE
            if ( createInterface->SetActiveViewport(hwnd) ) {
                return FALSE;
            }

            if (createInterface->IsCPEdgeOnInView()) { 
                res = FALSE;
                goto done;
            }

            // if lights were hidden by category, re-display them
            GetCOREInterface()->SetHideByCategoryFlags(
                    GetCOREInterface()->GetHideByCategoryFlags() & ~HIDE_LIGHTS);

            if ( attachedToNode ) {
                // send this one on its way
                lgtObject->EndEditParams( (IObjParam*)createInterface, 0, NULL);
                macroRec->EmitScript();  // JBW 4/23/99
                    
                // Get rid of the reference.
                if (lgtNode)
                    DeleteReference(0);

                // new object
                CreateNewObject();   // creates lgtObject
            }

            needToss = theHold.GetGlobalPutCount()!=lastPutCount;

            theHold.Begin();     // begin hold for undo
            mat.IdentityMatrix();

            // link it up
            lgtNode = createInterface->CreateObjectNode( lgtObject);
            attachedToNode = TRUE;
            assert( lgtNode );                  
            createCB = lgtObject->GetCreateMouseCallBack();                 
            createInterface->SelectNode( lgtNode );
                    
            // Create target object and node
            targObject = TRACKED_NEW TargetObject;
            assert(targObject);
            targNode = createInterface->CreateObjectNode( targObject);
            assert(targNode);
            targName = lgtNode->GetName();
            targName += GetString(IDS_DB_DOT_TARGET);
            targNode->SetName(targName);
                
            // hook up camera to target using lookat controller.
            createInterface->BindToTarget(lgtNode,targNode);

            // Reference the new node so we'll get notifications.
            MakeRefByID( FOREVER, 0, lgtNode);
            
            // Position camera and target at first point then drag.
            mat.IdentityMatrix();
            //mat[3] = vpx->GetPointOnCP(m);
            #ifdef _3D_CREATE
                mat.SetTrans( vpx->SnapPoint(m,m,NULL,SNAP_IN_3D) );
            #else
                mat.SetTrans(vpx->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
            #endif
            createInterface->SetNodeTMRelConstPlane(lgtNode, mat);
            createInterface->SetNodeTMRelConstPlane(targNode, mat);
            lgtObject->Enable(1);

            ignoreSelectionChange = TRUE;
            createInterface->SelectNode( targNode,0);
            ignoreSelectionChange = FALSE;
            res = TRUE;
            break;
                    
        case 1:
            if (Length(m-pt0)<2)
                goto abort;
            //mat[3] = vpx->GetPointOnCP(m);
            #ifdef _3D_CREATE
                mat.SetTrans( vpx->SnapPoint(m,m,NULL,SNAP_IN_3D) );
            #else
                mat.SetTrans(vpx->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
            #endif
            macroRec->Disable();   // JBW 4/23/99
            createInterface->SetNodeTMRelConstPlane(targNode, mat);
            macroRec->Enable();

            ignoreSelectionChange = TRUE;
            createInterface->SelectNode( lgtNode);
            ignoreSelectionChange = FALSE;
                    
            theHold.Accept(IDS_DS_CREATE);   

            createInterface->AddLightToScene(lgtNode); 
            createInterface->RedrawViews(createInterface->GetTime());  

            res = FALSE;    // We're done
            break;
        }           
        break;

    case MOUSE_MOVE:
        //mat[3] = vpx->GetPointOnCP(m);
        #ifdef _3D_CREATE
            mat.SetTrans( vpx->SnapPoint(m,m,NULL,SNAP_IN_3D) );
        #else
            mat.SetTrans(vpx->SnapPoint(m,m,NULL,SNAP_IN_PLANE));
        #endif
        macroRec->Disable();   // JBW 4/23/99
        createInterface->SetNodeTMRelConstPlane(targNode, mat);
        macroRec->Enable();
        createInterface->RedrawViews(createInterface->GetTime());

        macroRec->SetProperty(lgtObject, _T("target"),   // JBW 4/23/99
            mr_create, Class_ID(TARGET_CLASS_ID, 0), GEOMOBJECT_CLASS_ID, 1, _T("transform"), mr_matrix3, &mat);

        res = TRUE;
        break;

    case MOUSE_FREEMOVE:
        SetCursor(LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CROSS_HAIR)));
        #ifdef _OSNAP
            //Snap Preview
            #ifdef _3D_CREATE
                vpx->SnapPreview(m,m,NULL, SNAP_IN_3D);
            #else
                vpx->SnapPreview(m,m,NULL, SNAP_IN_PLANE);
            #endif
        #endif
        vpx->TrackImplicitGrid(m); //KENNY MERGE
        break;

    case MOUSE_PROPCLICK:
        // right click while between creations
        createInterface->RemoveMode(NULL);
        break;
        
    case MOUSE_ABORT:
        abort:
        assert( lgtObject );
        lgtObject->EndEditParams( (IObjParam*)createInterface,0,NULL);
        // Toss the undo stack if param changes have been made
        macroRec->Cancel();  // JBW 4/23/99
        theHold.Cancel();    // deletes both the camera and target.
        if (needToss) 
            GetSystemSetting(SYSSET_CLEAR_UNDO);
        lgtNode = NULL;         
        targNode = NULL;        
        createInterface->RedrawViews(createInterface->GetTime()); 
        CreateNewObject();  
        attachedToNode = FALSE;
        res = FALSE;                        
    }
    
done:
    //KENNY MERGE
    if ((res == CREATE_STOP)||(res==CREATE_ABORT))
        vpx->ReleaseImplicitGrid();
    createInterface->ReleaseViewport(vpx); 
    return res;
}


#endif