// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "std3d.h"
#include "nel/3d/point_light_model.h"
#include "nel/3d/light_trav.h"
#include "nel/3d/root_model.h"
#include "nel/3d/skeleton_model.h"
#include "nel/3d/scene.h"
using namespace NLMISC;
namespace NL3D {
// ***************************************************************************
void CPointLightModel::registerBasic()
{
CScene::registerModel( PointLightModelId, TransformId, CPointLightModel::creator);
}
// ***************************************************************************
CPointLightModel::CPointLightModel()
{
_DeltaPosToSkeletonWhenOutOfFrustum.set(0, 0, 1.5f);
_TimeFromLastClippedSpotDirection= 0;
_InfluenceLightMap= false;
}
// ***************************************************************************
CPointLightModel::~CPointLightModel()
{
}
// ***************************************************************************
void CPointLightModel::initModel()
{
CTransform::initModel();
// link me to the root of light.
getOwnerScene()->getLightTrav().addPointLightModel(this);
}
// ***************************************************************************
void CPointLightModel::setDeltaPosToSkeletonWhenOutOfFrustum(const CVector &deltaPos)
{
_DeltaPosToSkeletonWhenOutOfFrustum= deltaPos;
}
// ***************************************************************************
const CVector &CPointLightModel::getDeltaPosToSkeletonWhenOutOfFrustum() const
{
return _DeltaPosToSkeletonWhenOutOfFrustum;
}
// ***************************************************************************
void CPointLightModel::traverseLight()
{
CLightTrav &lightTrav= getOwnerScene()->getLightTrav();
// Note: any dynamic light is supposed to always move each frame, so they are re-inserted in the
// quadGrid each frame.
// reset all models lighted by this light.
// Then models are marked dirty and their light setup is reseted
PointLight.resetLightedModels();
// if the light is visible (ie not hiden)
if( isHrcVisible() )
{
// If the light is not hidden by any skeleton.
if( isClipVisible() )
{
// recompute the worldPosition of the light.
PointLight.setPosition( getWorldMatrix().getPos() );
// recompute the worldSpotDirection of the light.
if(PointLight.getType() == CPointLight::SpotLight)
{
// Interpolate over time. (hardcoded)
_TimeFromLastClippedSpotDirection-= 0.05f;
if(_TimeFromLastClippedSpotDirection <= 0)
{
PointLight.setupSpotDirection(getWorldMatrix().getJ());
}
else
{
CVector actualSpotDir= getWorldMatrix().getJ();
// Interpolate
float t= _TimeFromLastClippedSpotDirection;
CVector interpSpotDir= actualSpotDir*(1-t) + _LastWorldSpotDirectionWhenOutOfFrustum * t;
// set the interpolated one.
PointLight.setupSpotDirection(interpSpotDir);
}
}
}
else
{
// We are hidden because a skeleton has hide us, or created during anim detail traversal (or else don't know why).
// NB: a point light can't be in a cluster since it is not a transformShape...
if (!_AncestorSkeletonModel) return;
const CMatrix &skMatrix= _AncestorSkeletonModel->getWorldMatrix();
PointLight.setPosition( skMatrix * _DeltaPosToSkeletonWhenOutOfFrustum );
// recompute the worldSpotDirection of the light.
if(PointLight.getType() == CPointLight::SpotLight)
{
// If last frame, this pointLight was visible (Time is not 1)
if(_TimeFromLastClippedSpotDirection != 1)
{
// Take the current World spot direction
_LastWorldSpotDirectionWhenOutOfFrustum= PointLight.getSpotDirection();
// reset time.
_TimeFromLastClippedSpotDirection= 1;
}
// Don't need to modify PointLight spot direction since already setuped (when model was visible)
}
}
// now, insert this light in the quadGrid. NB: in CLightTrav::traverse(), the quadGrid is cleared before here.
// This light will touch (resetLighting()) any model it may influence.
lightTrav.LightingManager.addDynamicLight(&PointLight);
// if this light is the one that should influence the lightmapped objects
if(_InfluenceLightMap)
{
// setup a light with attenuation, for more accurate vertex lighting
CLight vertexLight;
// setup position/type/direction/color
if(PointLight.getType()!=CPointLight::SpotLight)
vertexLight.setupPointLight(CRGBA::Black, PointLight.getDiffuse(), PointLight.getSpecular(),
PointLight.getPosition(), CVector::K);
else
vertexLight.setupSpotLight(CRGBA::Black, PointLight.getDiffuse(), PointLight.getSpecular(),
PointLight.getPosition(), PointLight.getSpotDirection(), 1, float(Pi)/2);
// setup attenuation
vertexLight.setupAttenuation(PointLight.getAttenuationBegin(), PointLight.getAttenuationEnd());
// setup hotspot for spotLight
if(PointLight.getType()==CPointLight::SpotLight)
{
// hotspot == where attenuation should be 0.9
vertexLight.setupSpotExponent(PointLight.getSpotAngleBegin()*0.9f + PointLight.getSpotAngleEnd()*0.1f);
}
// setup the driver
getOwnerScene()->getDriver()->setLightMapDynamicLight(true, vertexLight);
}
}
}
} // NL3D