123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "T3D/spotLight.h"
- #include "console/consoleTypes.h"
- #include "core/stream/bitStream.h"
- #include "gfx/gfxDrawUtil.h"
- IMPLEMENT_CO_NETOBJECT_V1( SpotLight );
- ConsoleDocClass( SpotLight,
- "@brief Lighting object which emits conical light in a direction.\n\n"
- "SpotLight is one of the two types of lighting objects that can be added "
- "to a Torque 3D level, the other being PointLight. Unlike directional or "
- "point lights, the SpotLights emits lighting in a specific direction "
- "within a cone. The distance of the cone is controlled by the SpotLight::range "
- "variable.\n\n"
- "@tsexample\n"
- "// Declaration of a point light in script, or created by World Editor\n"
- "new SpotLight(SampleSpotLight)\n"
- "{\n"
- " range = \"10\";\n"
- " innerAngle = \"40\";\n"
- " outerAngle = \"45\";\n"
- " isEnabled = \"1\";\n"
- " color = \"1 1 1 1\";\n"
- " brightness = \"1\";\n"
- " castShadows = \"0\";\n"
- " priority = \"1\";\n"
- " animate = \"1\";\n"
- " animationPeriod = \"1\";\n"
- " animationPhase = \"1\";\n"
- " flareType = \"LightFlareExample0\";\n"
- " flareScale = \"1\";\n"
- " attenuationRatio = \"0 1 1\";\n"
- " shadowType = \"Spot\";\n"
- " texSize = \"512\";\n"
- " overDarkFactor = \"2000 1000 500 100\";\n"
- " shadowDistance = \"400\";\n"
- " shadowSoftness = \"0.15\";\n"
- " numSplits = \"1\";\n"
- " logWeight = \"0.91\";\n"
- " fadeStartDistance = \"0\";\n"
- " lastSplitTerrainOnly = \"0\";\n"
- " representedInLightmap = \"0\";\n"
- " shadowDarkenColor = \"0 0 0 -1\";\n"
- " includeLightmappedGeometryInShadow = \"0\";\n"
- " position = \"-29.4362 -5.86289 5.58602\";\n"
- " rotation = \"1 0 0 0\";\n"
- "};\n"
- "@endtsexample\n\n"
- "@see LightBase\n\n"
- "@see PointLight\n\n"
- "@ingroup Lighting\n"
- );
- SpotLight::SpotLight()
- : mRange( 10.0f ),
- mInnerConeAngle( 40.0f ),
- mOuterConeAngle( 45.0f )
- {
- // We set the type here to ensure the extended
- // parameter validation works when setting fields.
- mLight->setType( LightInfo::Spot );
- }
- SpotLight::~SpotLight()
- {
- }
- void SpotLight::initPersistFields()
- {
- docsURL;
- addGroup( "Light" );
-
- addField( "range", TypeF32, Offset( mRange, SpotLight ) );
- addField( "innerAngle", TypeF32, Offset( mInnerConeAngle, SpotLight ) );
- addField( "outerAngle", TypeF32, Offset( mOuterConeAngle, SpotLight ) );
- endGroup( "Light" );
- // We do the parent fields at the end so that
- // they show up that way in the inspector.
- Parent::initPersistFields();
- // Remove the scale field... it's already
- // defined by the range and angle.
- removeField( "scale" );
- //These are particular fields for PSSM, so useless for point lights
- removeField("numSplits");
- removeField("logWeight");
- removeField("lastSplitTerrainOnly");
- }
- void SpotLight::_conformLights()
- {
- mLight->setTransform( getTransform() );
- mRange = getMax( mRange, 0.05f );
- mLight->setRange( mRange );
- mLight->setColor( mColor );
- mLight->setBrightness( mBrightness );
- mLight->setCastShadows( mCastShadows );
- mLight->setStaticRefreshFreq(mStaticRefreshFreq);
- mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
- mLight->setPriority( mPriority );
- mOuterConeAngle = getMax( 0.01f, mOuterConeAngle );
- mInnerConeAngle = getMin( mInnerConeAngle, mOuterConeAngle );
- mLight->setInnerConeAngle( mInnerConeAngle );
- mLight->setOuterConeAngle( mOuterConeAngle );
- // Update the bounds and scale to fit our spotlight.
- F32 radius = mRange * mSin( mDegToRad( mOuterConeAngle ) * 0.5f );
- Point3F objectScale(radius, mRange, radius);
- Point3F objectBoxMin(-1, 0, -1);
- if (mAnimationData && mAnimationData->mRot.keyLen[0] > 0)
- {
- objectBoxMin.set(-1, -1, -1);
- objectScale.set(mRange, mRange, mRange);
- }
- mObjBox.minExtents.set(objectBoxMin);
- mObjBox.maxExtents.set(1, 1, 1);
- mObjScale.set(objectScale);
- // Skip our transform... it just dirties mask bits.
- Parent::setTransform( mObjToWorld );
- }
- U32 SpotLight::packUpdate(NetConnection *conn, U32 mask, BitStream *stream )
- {
- if ( stream->writeFlag( mask & UpdateMask ) )
- {
- stream->write( mRange );
- stream->write( mInnerConeAngle );
- stream->write( mOuterConeAngle );
- }
-
- return Parent::packUpdate( conn, mask, stream );
- }
- void SpotLight::unpackUpdate( NetConnection *conn, BitStream *stream )
- {
- if ( stream->readFlag() ) // UpdateMask
- {
- stream->read( &mRange );
- stream->read( &mInnerConeAngle );
- stream->read( &mOuterConeAngle );
- }
-
- Parent::unpackUpdate( conn, stream );
- }
- void SpotLight::setScale( const VectorF &scale )
- {
- // The y coord is the spotlight range.
- mRange = getMax( scale.y, 0.05f );
- // Use the average of the x and z to get a radius. This
- // is the best method i've found to make the manipulation
- // from the WorldEditor gizmo to feel right.
- F32 radius = mClampF( ( scale.x + scale.z ) * 0.5f, 0.05f, mRange );
- mOuterConeAngle = mRadToDeg( mAsin( radius / mRange ) ) * 2.0f;
- // Make sure the inner angle is less than the outer.
- //
- // TODO: Maybe we should make the inner angle a scale
- // and not an absolute angle?
- //
- mInnerConeAngle = getMin( mInnerConeAngle, mOuterConeAngle );
- // We changed a bunch of our settings
- // so notify the client.
- setMaskBits( UpdateMask );
- // Let the parent do the final scale.
- Parent::setScale( VectorF( radius, mRange, radius ) );
- }
- void SpotLight::_renderViz( SceneRenderState *state )
- {
- GFXDrawUtil *draw = GFX->getDrawUtil();
- GFXStateBlockDesc desc;
- desc.setZReadWrite( true, false );
- desc.setCullMode( GFXCullNone );
- desc.setBlend( true );
- // Base the color on the light color.
- ColorI color = mColor.toColorI();
- color.alpha = 16;
- F32 radius = mRange * mSin( mDegToRad( mOuterConeAngle * 0.5f ) );
- draw->drawCone( desc,
- getPosition() + ( getTransform().getForwardVector() * mRange ),
- getPosition(),
- radius,
- color );
- }
|