You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

904 lines
28 KiB

/*==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/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
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==*/
///////////////////////////////////////////////////////////////////////////////
// //
// plRealTimeLights.cpp - Implementation for some MAX RT lights //
// Cyan, Inc. //
// //
//// Version History //////////////////////////////////////////////////////////
// //
// 8.2.2001 mcn - Created. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "HeadSpin.h"
#include "plRealTimeLights.h"
#pragma hdrstop
#include "MaxPlasmaMtls/Layers/plLayerTex.h"
#include "MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h"
#include "MaxComponent/plMaxAnimUtils.h"
#include "plGLight/plLightKonstants.h"
void DummyLightCodeIncludeFunc() {}
//// Static ClassDesc2 Get Functions //////////////////////////////////////////
plRTOmniLightDesc plRTOmniLightDesc::fStaticDesc;
plRTSpotLightDesc plRTSpotLightDesc::fStaticDesc;
plRTDirLightDesc plRTDirLightDesc::fStaticDesc;
//// ParamBlock2 DlgProc Functions ////////////////////////////////////////////
const char* DecayLevel[] = {
"None",
"Inverse",
"Inverse Square",
NULL
};
const char* ShadowState[] = {
"Shadow Map",
NULL
};
class LightDlgProc : public plBaseLightProc
{
protected:
void IValidateSpinners( TimeValue t, WPARAM wParam, IParamBlock2 *pb, IParamMap2 *map )
{
/// Make sure falloff is >= hotspot (adjust the one we're not editing)
float hotspot, falloff;
hotspot = pb->GetFloat( plRTLightBase::kHotSpot, t );
falloff = pb->GetFloat( plRTLightBase::kFallOff, t );
if( falloff < hotspot )
{
if( LOWORD( wParam ) == IDC_LHOTSIZESPINNER )
pb->SetValue( plRTLightBase::kFallOff, t, hotspot );
else
pb->SetValue( plRTLightBase::kHotSpot, t, falloff );
map->Invalidate( plRTLightBase::kHotSpot );
map->Invalidate( plRTLightBase::kFallOff );
}
IBuildLightMesh( (plRTLightBase *)pb->GetOwner(), falloff );
}
public:
BOOL 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_COMMAND:
if(LOWORD(wParam) == IDC_PROJ_MAPNAME )
{
if( gl->ClassID() != RTOMNI_LIGHT_CLASSID )
// if(pb->GetInt(plRTLightBase::kLightType) != plRTLightBase::RT_OMNI)
// map->SetValue(plRTSpotLight::kProjMapTexButton, t,
//gl->SetProjMap(
map->Invalidate(plRTSpotLight::kProjMapTexButton);
return false;
}
else if( LOWORD( wParam ) == IDC_LHOTSIZE || LOWORD( wParam ) == IDC_LFALLOFF )
{
if( HIWORD( wParam ) == EN_CHANGE )
IValidateSpinners( t, wParam, pb, map );
}
break;
case CC_SPINNER_CHANGE:
if( LOWORD( wParam ) == IDC_LHOTSIZESPINNER || LOWORD( wParam ) == IDC_LFALLOFFSPINNER )
IValidateSpinners( t, wParam, pb, map );
break;
}
return plBaseLightProc::DlgProc( t, map, hWnd, msg, wParam, lParam );;
}
void DeleteThis() {};
};
static LightDlgProc gLiteDlgProc;
// Having these includes down here is a hack.
#include "plRealTimeLightsPBDec.h"
#include "plRTObjLightDesc.h"
///////////////////////////////////////////////////////////////////////
//
// Light Descriptors declared below for the different plRTLights...
//
//
//
//
//--- Base Light Class derived from the ObjLightDesc
#define COS_45 0.7071067f
#define COS_45_2X 1.4142136f
static float stepFactor[] = {50.0f,80.0f,140.0f};
#define MAXSTEPS 1000
BaseObjLight::BaseObjLight(INode *n) : ObjLightDesc(n)
{
ObjectState os = n->EvalWorldState(TimeValue(0));
assert(os.obj->SuperClassID()==LIGHT_CLASS_ID);
gl = (os.obj->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTLightBase*)os.obj->GetReference(0) : (plRTLightBase*)os.obj; // JBW 4/7/99
}
static Color blackCol(0,0,0);
int BaseObjLight::Update(TimeValue t, const RendContext& rc, RenderGlobalContext * rgc, BOOL shadows, BOOL shadowGeomChanged) {
ObjLightDesc::Update(t,rc,rgc,shadows,shadowGeomChanged);
intensCol = ls.intens*ls.color*rc.GlobalLightLevel();
ObjectState os = inode->EvalWorldState(t);
plRTLightBase* lob = (plRTLightBase *)os.obj;
contrast = 0; //lob->GetParamBlock2()->GetFloat(plRTLightBase::kContrast, t);
diffSoft = 0; //lob->GetDiffuseSoft(t)/100.0f;
float a = .5; //contrast/204.0f + 0.5f; // so "a" varies from .5 to .99
kA = (2.0f-1.0f/a);
kB = 1.0f-kA;
diffSoften = false; //lob->GetParamBlock2()->GetInt(plRTLightBase::kDiffOn, t); //GetSoftenDiffuse();
return 1;
}
//--- Omni Light ------------------------------------------------
OmniLight::OmniLight(INode *inode, BOOL forceShadowBuf ) : BaseObjLight(inode){
//projector = /*doShadows = shadowRay =*/ FALSE;
//projMap = NULL;
needMultiple = FALSE;
}
OmniLight::~OmniLight()
{
}
int OmniLight::UpdateViewDepParams(const Matrix3& worldToCam) {
BaseObjLight::UpdateViewDepParams(worldToCam);
return 1;
}
static Point3 MapToDir(Point3 p, int k) {
switch(k) {
case 0: return Point3( p.z, p.y, -p.x); // +X
case 1: return Point3( -p.z, p.y, p.x); // -X
case 2: return Point3( p.x, p.z, -p.y); // +Y
case 3: return Point3( p.x,-p.z, p.y); // -Y
case 4: return Point3( -p.x, p.y, -p.z); // +Z
case 5: return p; // -Z
}
return p;
}
static void GetMatrixForDir(Matrix3 &origm, Matrix3 &tm, int k ) {
tm = origm;
switch(k) {
case 0: tm.PreRotateY(-HALFPI); break; // Map 0: +X axis
case 1: tm.PreRotateY( HALFPI); break; // Map 1: -X axis
case 2: tm.PreRotateX( HALFPI); break; // Map 2: +Y axis
case 3: tm.PreRotateX(-HALFPI); break; // Map 3: -Y axis
case 4: tm.PreRotateY( PI ); break; // Map 4: +Z axis
case 5: break; // Map 5: -Z axis
}
}
static int WhichDir(Point3 &p) {
int j = MaxComponent(p); // the component with the maximum abs value
return (p[j]<0.0f) ? 2*j+1 : 2*j;
}
int OmniLight::Update(TimeValue t, const RendContext & rc,
RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged)
{
BaseObjLight::Update(t,rc,rgc,shadows,shadowGeomChanged);
ObjectState os = inode->EvalWorldState(t);
LightObject* lob = (LightObject *)os.obj;
assert(os.obj->SuperClassID()==LIGHT_CLASS_ID);
plRTOmniLight* gl = (lob->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTOmniLight*)lob->GetReference(0) : (plRTOmniLight*)lob; // JBW 4/7/99
decayType = gl->GetDecayType();
decayRadius = gl->GetDecayRadius(t);
fov = HALFPI; // 90 degree fov
int res=1;
if(gl->GetTex())
gl->GetTex()->Update(t, FOREVER);
//projector = gl->GetProjector();
//if (projector){
// projMap = gl->GetProjMap();
// if( projMap ) projMap->Update(t,FOREVER);
//}
return res;
}
////------------------------------------------------------------------
//
//
// SpotLight descriptors.....
//
//
//
//
//
SpotLight::SpotLight(INode *inode, BOOL forceShadowBuf ):BaseObjLight(inode)
{
projMap = NULL;
}
int SpotLight::Update(TimeValue t, const RendContext &rc, RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged)
{
int res = 1;
BaseObjLight::Update(t,rc,rgc,shadows, shadowGeomChanged);
float hs = DegToRad(ls.hotsize);
float fs = DegToRad(ls.fallsize);
fall_tan = (float)tan(fs/2.0f);
hot_cos = (float)cos(hs/2.0f);
fall_cos =(float)cos(fs/2.0f);
fall_sin = (float)sin(fs/2.0f);
hotpct = ls.hotsize/ls.fallsize;
ihotpct = 1.0f - hotpct;
ObjectState os = inode->EvalWorldState(t);
LightObject* lob = (LightObject *)os.obj;
assert(os.obj->SuperClassID()==LIGHT_CLASS_ID);
plRTLightBase* gl = (lob->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTLightBase*)lob->GetReference(0) : (plRTLightBase*)lob; // JBW 4/7/99
decayType = gl->GetDecayType();
decayRadius = gl->GetDecayRadius(t);
projector = gl->GetProjector();
fov = std::max(fs, hs);
float aspect = 1.0f;
fov = 2.0f* (float)atan(tan(fov*0.5f)*sqrt(aspect));
zfac = -sz2 /(float)tan(0.5*(double)fov);
xscale = zfac;
yscale = -zfac*aspect;
curve =(float)fabs(1.0f/xscale);
rectv0.y = fall_sin * (float)sqrt(aspect);
rectv1.y = fall_sin / (float)sqrt(aspect);
rectv0.x = rectv1.x = fall_cos;
rectv0 = Normalize(rectv0);
rectv1 = Normalize(rectv1);
Interval v;
if (projector){
projMap = gl->GetProjMap();
if( projMap ) projMap->Update(t,v);
}
return res;
}
int SpotLight::UpdateViewDepParams(const Matrix3& worldToCam)
{
BaseObjLight::UpdateViewDepParams(worldToCam);
lightDir = -FNormalize(lightToCam.GetRow(2));
return 1;
}
BOOL SpotLight::IsFacingLight(Point3 &dir)
{
return dir.z>0.0f;
}
//--- Directional Light ------------------------------------------------
DirLight::DirLight(INode *inode, BOOL forceShadowBuf ) : BaseObjLight(inode)
{
projMap = NULL;
}
int DirLight::Update(TimeValue t, const RendContext &rc,
RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged)
{
int res = 1;
BaseObjLight::Update(t,rc,rgc,shadows,shadowGeomChanged);
hotsz = ls.hotsize;
fallsz = ls.fallsize;
fallsq = fallsz*fallsz;
hotpct = ls.hotsize/ls.fallsize;
ihotpct = 1.0f - hotpct;
ObjectState os = inode->EvalWorldState(t);
LightObject* lob = (LightObject *)os.obj;
assert(os.obj->SuperClassID()==LIGHT_CLASS_ID);
plRTDirLight* gl = (lob->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTDirLight*)lob->GetReference(0) : (plRTDirLight*)lob; // JBW 4/7/99
//projector = gl->GetProjector();
aspect = 1.0f;
//if (projector){
// projMap = gl->GetProjMap();
// if( projMap ) projMap->Update(t,FOREVER);
// }
return res;
};
int DirLight::UpdateViewDepParams(const Matrix3& worldToCam) {
BaseObjLight::UpdateViewDepParams(worldToCam);
lightDir = FNormalize(lightToCam.GetRow(2));
return 1;
}
////////////////////////////////////////////////////////////////////////////////////
//
// plRTOmni Stuff below
//
//
//
//
//
plRTOmniLight::plRTOmniLight()
{
fIP = NULL;
fLightPB = NULL;
fClassDesc = plRTOmniLightDesc::GetDesc();
fClassDesc->MakeAutoParamBlocks(this);
fLightPB->SetValue(kLightColor, 0, Color(255,255,255));
SetHSVColor(0, Point3(255, 255, 255));
fTex = NULL;
meshBuilt = 0;
IBuildMeshes(true);
}
ObjLightDesc *plRTOmniLight::CreateLightDesc(INode *n, BOOL forceShadowBuf)
{
return new OmniLight( n, forceShadowBuf );
}
RefTargetHandle plRTOmniLight::Clone(RemapDir &remap)
{
plRTOmniLight *obj = new plRTOmniLight;//(plRTLightBase*) fClassDesc->Create(false);
obj->ReplaceReference(kRefSpotLight, fLightPB->Clone(remap));
BaseClone(this, obj, remap);
return obj;
}
void plRTOmniLight::IBuildMeshes( BOOL isnew )
{
BuildStaticMeshes();
fMesh = staticMesh[ plRTLightBase::RT_OMNI ];
}
//// DrawConeAndLine /////////////////////////////////////////////////////////
int plRTOmniLight::DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing )
{
float atOneHalfDist;
Matrix3 tm = inode->GetObjectTM( t );
gw->setTransform( tm );
gw->clearHitCode();
if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) )
{
if( GetUseAtten() )
{
// Draw hotspot as the point at which light is 1/2 intensity (just to help the visual)
gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) );
if( fLightPB->GetInt( kAttenTypeRadio, t ) == 0 )
atOneHalfDist = GetAtten( t, ATTEN_END ) / ( fLightPB->GetFloat( kIntensity, t ) * plSillyLightKonstants::GetFarPowerKonst() - 1.f );
else
atOneHalfDist = sqrt( GetAtten( t, ATTEN_END ) * GetAtten( t, ATTEN_END ) / ( fLightPB->GetFloat( kIntensity, t ) * plSillyLightKonstants::GetFarPowerKonst() - 1.f ) );
if( atOneHalfDist > 0.0f )
DrawCone( t, gw, atOneHalfDist );
gw->setColor( LINE_COLOR, GetUIColor( COLOR_FALLOFF ) );
DrawCone( t, gw, GetAtten( t, ATTEN_END ) );
}
else
DrawArrows( t, gw, 50 );
}
return gw->checkHitCode();
}
//// DrawCone ////////////////////////////////////////////////////////////////
// Function called by MAX to render the cone shape in the viewport for this
// light. Note that this is the cone, not the actual object itself!
void plRTOmniLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist )
{
Point3 pts[ NUM_CIRC_PTS * 3 + 1 ];
/// Draw sphere-thingy
GetAttenPoints( t, dist, pts );
gw->polyline( NUM_CIRC_PTS, pts, nil, nil, true, nil );
gw->polyline( NUM_CIRC_PTS, pts + NUM_CIRC_PTS, nil, nil, true, nil );
gw->polyline( NUM_CIRC_PTS, pts + 2 * NUM_CIRC_PTS, nil, nil, true, nil );
}
//// DrawArrows //////////////////////////////////////////////////////////////
// Renders some arrows in all directions, to show a radiating, attenuation-less
// omni light.
void plRTOmniLight::DrawArrows( TimeValue t, GraphicsWindow *gw, float dist )
{
Point3 directions[] = { Point3( 1, 0, 0 ), Point3( -1, 0, 0 ), Point3( 0, 1, 0 ), Point3( 0, -1, 0 ),
Point3( 0, 0, 1 ), Point3( 0, 0, -1 ),
Point3( 2, 2, 0 ), Point3( 2, -2, 0 ), Point3( 2, 0, 2 ), Point3( 2, 0, -2 ),
Point3( -2, 2, 0 ), Point3( -2, -2, 0 ), Point3( -2, 0, 2 ), Point3( -2, 0, -2 ),
Point3( 0, 2, 2 ), Point3( 0, 2, -2 ), Point3( 0, -2, 2 ), Point3( 0, -2, -2 ),
Point3( 0, 0, 0 ) };
Point3 empty( 0, 0, 0 );
int i;
Point3 pts[ 5 ];
/// Adjust directions
for( i = 0; directions[ i ] != empty; i++ )
{
if( directions[ i ].x == 2.f )
directions[ i ].x = 0.7f;
else if( directions[ i ].x == -2.f )
directions[ i ].x = -0.7f;
if( directions[ i ].y == 2.f )
directions[ i ].y = 0.7f;
else if( directions[ i ].y == -2.f )
directions[ i ].y = -0.7f;
if( directions[ i ].z == 2.f )
directions[ i ].z = 0.7f;
else if( directions[ i ].z == -2.f )
directions[ i ].z = -0.7f;
}
/// Draw da arrows
gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) );
for( i = 0; directions[ i ] != empty; i++ )
DrawArrow( t, gw, directions[ i ], dist );
}
//// GetLocalBoundBox ////////////////////////////////////////////////////////
void plRTOmniLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box )
{
Point3 loc = node->GetObjectTM( t ).GetTrans();
float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f;
float width;
box = fMesh.getBoundingBox();
// Because we want to scale about the origin, not the box center, we have to do this funky offset
Point3 boxCenter = box.Center();
box.Translate( -boxCenter );
box.Scale( scaleFactor );
boxCenter *= scaleFactor;
box.Translate( boxCenter );
// Include points for the spotlight. That means either the attenuated cone or
// our unattenuated cone display
if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) )
{
if( GetUseAtten() )
width = GetAtten( t, ATTEN_END );
else
width = 50.f; // Include our arrows
box += Point3( -width, -width, -width );
box += Point3( width, width, width );
}
}
////////////////////////////////////////////////////////////////////////////////////////
//
//
// SpotLight Stuff
//
//
//// Local Copy of shared Anim PB /////////////////////////////////////////////
plRTSpotLight::plRTSpotLight()
{
fIP = NULL;
fLightPB = NULL;
fClassDesc = plRTSpotLightDesc::GetDesc();
fClassDesc->MakeAutoParamBlocks(this);
fLightPB->SetValue(kLightColor, 0, Color(255,255,255));
SetHSVColor(0, Point3(255, 255, 255));
fTex = NULL;
meshBuilt = 0;
IBuildMeshes(true);
}
ObjLightDesc *plRTSpotLight::CreateLightDesc(INode *n, BOOL forceShadowBuf)
{
return new SpotLight( n, forceShadowBuf );
}
RefTargetHandle plRTSpotLight::Clone(RemapDir &remap)
{
plRTSpotLight *obj = new plRTSpotLight;//(plRTLightBase*) fClassDesc->Create(false);
obj->ReplaceReference(kRefSpotLight, fLightPB->Clone(remap));
BaseClone(this, obj, remap);
return obj;
}
Texmap *plRTSpotLight::GetProjMap()
{
if( !fLightPB->GetInt( kUseProjectorBool ) )
return nil;
Interval valid = Interval(0,0);
if( !GetTex() )
{
if( fLightPB->GetInt( kUseProjectorBool ) )
{
PBBitmap* bitmap = fLightPB->GetBitmap( kProjMapTexButton, 0 );
SetProjMap( &bitmap->bi );
}
}
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 plRTSpotLight::IBuildMeshes( BOOL isnew )
{
float val = fLightPB->GetFloat( kHotSpot, TimeValue(0) ); //Init val of HotSpot
if( isnew )
{
val = fLightPB->GetFloat(kHotSpot, TimeValue(0));
SetHotspot( TimeValue(0), val);
//val = 45.0;
val = fLightPB->GetFloat(kFallOff, TimeValue(0));
SetFallsize( TimeValue(0), val);
val = fLightPB->GetFloat(kAttenMaxFalloffEdit, TimeValue(0)); //fLightPB->GetFloat(kTargetDist, TimeValue(0));
if(val < 1.0f)
SetTDist( TimeValue(0), DEF_TDIST);
else
SetTDist( TimeValue(0), val);
val = fLightPB->GetFloat(kHotSpot, TimeValue(0));
}
BuildSpotMesh( val );
fMesh = spotMesh;
}
//// DrawConeAndLine /////////////////////////////////////////////////////////
int plRTSpotLight::DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing )
{
Matrix3 tm = inode->GetObjectTM( t );
gw->setTransform( tm );
gw->clearHitCode();
if( extDispFlags & EXT_DISP_ONLY_SELECTED )
DrawCone( t, gw, GetAtten( t, ATTEN_END ) );
return gw->checkHitCode();
}
//// DrawCone ////////////////////////////////////////////////////////////////
// Function called by MAX to render the cone shape in the viewport for this
// light. Note that this is the cone, not the actual object itself!
void plRTSpotLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist )
{
int i;
Point3 pts[ NUM_CIRC_PTS + 1 ], u[ 3 ];
if( !GetUseAtten() )
{
/// Don't use atten, but still want a cone, so draw the cone w/ a dist of 100
/// and the lines extending past it (thus indicating that it keeps going)
dist = 100;
}
/// Draw hotspot cone
gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) );
GetConePoints( t, -1.0f, GetHotspot( t ), dist, pts );
gw->polyline( NUM_CIRC_PTS, pts, nil, nil, true, nil );
if( GetUseAtten() )
{
u[ 0 ] = Point3( 0, 0, 0 );
for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX )
{
u[ 1 ] = pts[ i ];
gw->polyline( 2, u, nil, nil, true, nil );
}
}
else
{
for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX )
{
pts[ i ] = pts[ i ].Normalize();
DrawArrow( t, gw, pts[ i ], dist + 50.f );
}
}
/// Draw falloff cone if necessary
if( GetHotspot( t ) < GetFallsize( t ) )
{
gw->setColor( LINE_COLOR, GetUIColor( COLOR_FALLOFF ) );
GetConePoints( t, -1.0f, GetFallsize( t ), dist, pts );
gw->polyline( NUM_CIRC_PTS, pts, nil, nil, true, nil );
if( GetUseAtten() )
{
u[ 0 ] = Point3( 0, 0, 0 );
for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX )
{
u[ 1 ] = pts[ i ];
gw->polyline( 2, u, nil, nil, true, nil );
}
}
else
{
for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX )
{
pts[ i ] = pts[ i ].Normalize();
DrawArrow( t, gw, pts[ i ], dist + 50.f );
}
}
}
}
//// GetLocalBoundBox ////////////////////////////////////////////////////////
void plRTSpotLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box )
{
Point3 loc = node->GetObjectTM( t ).GetTrans();
float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f;
float width, depth;
box = fMesh.getBoundingBox();
// Because we want to scale about the origin, not the box center, we have to do this funky offset
Point3 boxCenter = box.Center();
box.Translate( -boxCenter );
box.Scale( scaleFactor );
boxCenter *= scaleFactor;
box.Translate( boxCenter );
// Include points for the spotlight. That means either the attenuated cone or
// our unattenuated cone display
if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) )
{
if( GetUseAtten() )
depth = GetAtten( t, ATTEN_END );
else
depth = 100.f + 50.f; // Include arrows
width = depth * tan( DegToRad( GetFallsize( t ) / 2.f ) );
box += Point3( -width, -width, 0.f );
box += Point3( width, width, -depth );
}
}
///////////////////////////////////////////////////////////////////////////////
//// Directional Light ////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
plRTDirLight::plRTDirLight()
{
fIP = NULL;
fLightPB = NULL;
fClassDesc = plRTDirLightDesc::GetDesc();
fClassDesc->MakeAutoParamBlocks(this);
fLightPB->SetValue(kLightColor, 0, Color(255,255,255));
SetHSVColor(0, Point3(255, 255, 255));
fTex = NULL;
meshBuilt = 0;
IBuildMeshes(true);
}
ObjLightDesc *plRTDirLight::CreateLightDesc(INode *n, BOOL forceShadowBuf)
{
return new DirLight( n, forceShadowBuf );
}
RefTargetHandle plRTDirLight::Clone(RemapDir &remap)
{
plRTDirLight *obj = new plRTDirLight;//(plRTLightBase*) fClassDesc->Create(false);
obj->ReplaceReference(kRefDirLight, fLightPB->Clone(remap));
BaseClone(this, obj, remap);
return obj;
}
//// IBuildMeshes ////////////////////////////////////////////////////////////
void plRTDirLight::IBuildMeshes( BOOL isnew )
{
BuildStaticMeshes();
fMesh = staticMesh[ plRTLightBase::RT_OMNI + 1 ];
}
//// DrawCone ////////////////////////////////////////////////////////////////
// Function called by MAX to render the cone shape in the viewport for this
// light. Note that this is the cone, not the actual object itself!
void plRTDirLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist )
{
Point3 arrow[ 7 ];
int i, j, r;
float d;
const float spacing = 20.f;
// Draw some funky arrows to represent our direction
dist = 100.f;
gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) );
for( i = -2; i <= 2; i++ )
{
for( j = -2; j <= 2; j++ )
{
r = ( i * i ) + ( j * j );
if( r <= 4 )
{
d = dist * ( 5 - r ) / 5;
IBuildZArrow( i * spacing, j * spacing, -d, -10.f, arrow );
gw->polyline( 6, arrow, nil, nil, true, nil );
}
}
}
}
//// IBuildZArrow ////////////////////////////////////////////////////////////
void plRTDirLight::IBuildZArrow( float x, float y, float zDist, float arrowSize, Point3 *pts )
{
pts[ 0 ] = Point3( x, y, 0.f );
pts[ 1 ] = Point3( x, y, zDist );
pts[ 2 ] = Point3( x + arrowSize / 2.f, y, zDist - arrowSize );
pts[ 3 ] = Point3( x, y, zDist - arrowSize );
pts[ 4 ] = Point3( x, y + arrowSize / 2.f, zDist - arrowSize );
pts[ 5 ] = Point3( x, y, zDist );
}
//// GetLocalBoundBox ////////////////////////////////////////////////////////
void plRTDirLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box )
{
Point3 loc = node->GetObjectTM( t ).GetTrans();
float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f;
float width, height, depth;
box = fMesh.getBoundingBox();
// Because we want to scale about the origin, not the box center, we have to do this funky offset
Point3 boxCenter = box.Center();
box.Translate( -boxCenter );
box.Scale( scaleFactor );
boxCenter *= scaleFactor;
box.Translate( boxCenter );
if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) )
{
width = 2 * 20.f + ( 10.f / 2.f ); // Add in half arrow size
height = 2 * 20.f + ( 10.f / 2.f );
depth = 100.f;
box += Point3( -width, -height, 0.f );
box += Point3( width, height, -depth );
}
}