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.
907 lines
26 KiB
907 lines
26 KiB
14 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
13 years ago
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
14 years ago
|
|
||
|
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
|
||
13 years ago
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 years ago
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
13 years ago
|
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.
|
||
14 years ago
|
|
||
|
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 );
|
||
|
}
|
||
|
}
|