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.
903 lines
28 KiB
903 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 ); |
|
} |
|
}
|
|
|