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.

891 lines
25 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/>.
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"
#include "iparamm2.h"
#include "../MaxPlasmaMtls/Layers/plLayerTex.h"
#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h"
#include "../MaxComponent/plMaxAnimUtils.h"
#include "plRTLightBaseAnimDlgProc.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;
#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 = hsMaximum(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 TRACKED_NEW OmniLight( n, forceShadowBuf );
}
RefTargetHandle plRTOmniLight::Clone(RemapDir &remap)
{
plRTOmniLight *obj = TRACKED_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 TRACKED_NEW SpotLight( n, forceShadowBuf );
}
RefTargetHandle plRTSpotLight::Clone(RemapDir &remap)
{
plRTSpotLight *obj = TRACKED_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 TRACKED_NEW DirLight( n, forceShadowBuf );
}
RefTargetHandle plRTDirLight::Clone(RemapDir &remap)
{
plRTDirLight *obj = TRACKED_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 );
}
}