// Wild Magic Source Code
// David Eberly
// http://www.geometrictools.com
// Copyright (c) 1998-2007
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.  The license is available for reading at
// either of the locations:
//     http://www.gnu.org/copyleft/lgpl.html
//     http://www.geometrictools.com/License/WildMagicLicense.pdf
//
// Version: 4.0.0 (2006/06/28)

#include "WSceneBuilder.h"
#include "decomp.h"

//----------------------------------------------------------------------------
void WSceneBuilder::BuildAmbientLight ()
{
    // Get Max's global ambient light settings, and build the corresponding
    // Wild Magic light.
    Color kColor = m_pkMax->GetAmbient(0,FOREVER);
    Wm4::Light* pkLight = WM4_NEW Wm4::Light(Wm4::Light::LT_AMBIENT);
    pkLight->Ambient = Wm4::ColorRGB(kColor.r,kColor.g,kColor.b);
    m_spkScene->AttachLight(pkLight);
}
//----------------------------------------------------------------------------
void WSceneBuilder::BuildLight (INode* pkMaxNode, Wm4::Spatial* pkWm4Node)
{
    // Build a Wild Magic light corresponding to a light in the Max hierarchy.
    // Max's omnidirectional, directional and free spot lights are supported.
    // Note that Max assigns a node in the scene graph to each light, whereas
    // Wild Magic allows a list of lights to be attached to a node.  The
    // approach used here is to attach the Wild Magic lights to the node in
    // the Wild Magic scene graph which corresponds to the parent of the
    // light node in the Max scene graph.
    //
    // pkMaxNode:
    //     The Max node representing the light.
    // pkWm4Node:
    //     The Wild Magic node to which the corresponding light state will
    //     be applied.

    // get the light's parameters from Max
    ObjectState kObjectState = pkMaxNode->EvalWorldState(m_iTimeStart);
    GenLight* pkGenLight = (GenLight*)kObjectState.obj;
    LightState kMaxLightState;
    Interval kValid = FOREVER;
    pkGenLight->EvalLightState(m_iTimeStart,kValid,&kMaxLightState);

    Wm4::Light* pkWm4Light = 0;

    switch (pkGenLight->Type())
    {
    case OMNI_LIGHT:
        pkWm4Light = BuildPointLight(pkMaxNode);
        break;
    case TSPOT_LIGHT:
    case FSPOT_LIGHT:
        pkWm4Light = BuildSpotLight(pkMaxNode,kMaxLightState);
        break;
    case TDIR_LIGHT:
    case DIR_LIGHT:
        pkWm4Light = BuildDirectionalLight(pkMaxNode);
        break;
    default: // Light not supported
        assert(false);
        return;
    }
    
    // TO DO:  Re-add the On flag to Light?
    //pkWm4Light->On = (kMaxLightState.on != 0);
    pkWm4Light->Intensity = kMaxLightState.intens;
    
    // Max lights seem to specify only one color. Use this color for Wild
    // Magic light's specular and diffuse colors.
    if (kMaxLightState.affectDiffuse)
    {
        pkWm4Light->Diffuse = Wm4::ColorRGB(kMaxLightState.color.r,
            kMaxLightState.color.g,kMaxLightState.color.b);
    }
    if (kMaxLightState.affectSpecular)
    {
        pkWm4Light->Specular = Wm4::ColorRGB(kMaxLightState.color.r,
            kMaxLightState.color.g,kMaxLightState.color.b);
    }
    
    // Set the attenuation parameters. Max's decay model is simpler than Wild
    // Magic's in that the decay can be either constant, linear or quadratic,
    // but not a combination of all three
    if (pkGenLight->GetDecayType() != 0)
    {
        // TO DO:  Re-add the Attenuate flag to Light?
        //pkWm4Light->Attenuate = true;             
        Interval kValid;
        float fDecayRadius = pkGenLight->GetDecayRadius(m_iTimeStart,kValid);
        
        switch (pkGenLight->GetDecayType())
        {
        case 0:
            pkWm4Light->Constant = ATTENUATION;
            pkWm4Light->Linear = 0.0f;
            pkWm4Light->Quadratic = 0.0f;
            break;
        case 1:
            pkWm4Light->Constant = 0.0f;
            pkWm4Light->Linear = ATTENUATION/fDecayRadius;
            pkWm4Light->Quadratic = 0.0f;
            break;
        case 2:
            pkWm4Light->Constant = 0.0f;
            pkWm4Light->Linear = 0.0f;
            pkWm4Light->Quadratic = ATTENUATION/(fDecayRadius*fDecayRadius);
            break;
        } 
    }
    else if (pkGenLight->GetUseAtten())
    {
        // TO DO:  Re-add the Attenuate flag to Light?
        //pkWm4Light->Attenuate = true;             
        pkWm4Light->Constant = 0.0f;
        pkWm4Light->Linear = ATTENUATION/kMaxLightState.attenEnd;
        pkWm4Light->Quadratic = 0.0f;
    }

    pkWm4Node->AttachLight(pkWm4Light);
}
//----------------------------------------------------------------------------
Wm4::Vector3f WSceneBuilder::GetLightLocation (INode* pkNode)
{
    // evaluate the node's object transform in local coordinates 
    Matrix3 kLocal = pkNode->GetNodeTM(m_iTimeStart) *
        Inverse(pkNode->GetParentTM(m_iTimeStart));

    // decompose the transform into its affine parts
    AffineParts kAffParts;
    decomp_affine(kLocal,&kAffParts);

    // extract the translation component
    return Wm4::Vector3f(kAffParts.t.x,kAffParts.t.y,kAffParts.t.z);
}
//----------------------------------------------------------------------------
Wm4::Light* WSceneBuilder::BuildPointLight (INode* pkNode)
{
    Wm4::Light* pkLight = WM4_NEW Wm4::Light(Wm4::Light::LT_POINT);

    // set the light's location
    pkLight->Position = GetLightLocation(pkNode);
    
    return pkLight;
}
//----------------------------------------------------------------------------
Wm4::Light* WSceneBuilder::BuildSpotLight (INode* pkNode,
    LightState& rkLightState)
{
    Wm4::Light* pkLight = WM4_NEW Wm4::Light(Wm4::Light::LT_SPOT);

    // set the light's location and direction
    pkLight->Position = GetLightLocation(pkNode);
    pkLight->SetDirection(-Wm4::Vector3f::UNIT_Z);

    // set the light's angle
    pkLight->Angle = HALFPI*rkLightState.fallsize/180.0f;

    // Set the light's exponent.  This equation seems to give reasonable
    // correspondance between Max and Wild Magic.
    pkLight->Exponent = 128.0f*((rkLightState.fallsize -
        rkLightState.hotsize)/rkLightState.fallsize);
    
    return pkLight;
}
//----------------------------------------------------------------------------
Wm4::Light* WSceneBuilder::BuildDirectionalLight (INode* pkNode)
{
    Wm4::Light* pkLight = WM4_NEW Wm4::Light(Wm4::Light::LT_DIRECTIONAL);

    // set the light's direction
    pkLight->SetDirection(-Wm4::Vector3f::UNIT_Z);

    return pkLight;
}
//----------------------------------------------------------------------------
