mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
572 lines
17 KiB
C++
572 lines
17 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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 "environment/sun.h"
|
|
|
|
#include "gfx/bitmap/gBitmap.h"
|
|
#include "math/mathIO.h"
|
|
#include "core/stream/bitStream.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "console/engineAPI.h"
|
|
#include "scene/sceneManager.h"
|
|
#include "math/mathUtils.h"
|
|
#include "lighting/lightInfo.h"
|
|
#include "lighting/lightManager.h"
|
|
#include "scene/sceneRenderState.h"
|
|
#include "renderInstance/renderPassManager.h"
|
|
#include "sim/netConnection.h"
|
|
#include "environment/timeOfDay.h"
|
|
#include "gfx/gfxTransformSaver.h"
|
|
#include "materials/materialManager.h"
|
|
#include "materials/baseMatInstance.h"
|
|
#include "materials/sceneData.h"
|
|
#include "math/util/matrixSet.h"
|
|
|
|
|
|
IMPLEMENT_CO_NETOBJECT_V1(Sun);
|
|
|
|
ConsoleDocClass( Sun,
|
|
"@brief A global light affecting your entire scene and optionally renders a corona effect.\n\n"
|
|
|
|
"Sun is both the directional and ambient light for your entire scene.\n\n"
|
|
|
|
"@ingroup Atmosphere"
|
|
);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Sun::Sun()
|
|
{
|
|
mNetFlags.set(Ghostable | ScopeAlways);
|
|
mTypeMask = EnvironmentObjectType | LightObjectType | StaticObjectType;
|
|
|
|
mLightColor.set(0.7f, 0.7f, 0.7f);
|
|
mLightAmbient.set(0.3f, 0.3f, 0.3f);
|
|
mBrightness = 1.0f;
|
|
mSunAzimuth = 0.0f;
|
|
mSunElevation = 35.0f;
|
|
mCastShadows = true;
|
|
mStaticRefreshFreq = 250;
|
|
mDynamicRefreshFreq = 8;
|
|
|
|
mAnimateSun = false;
|
|
mTotalTime = 0.0f;
|
|
mCurrTime = 0.0f;
|
|
mStartAzimuth = 0.0f;
|
|
mEndAzimuth = 0.0f;
|
|
mStartElevation = 0.0f;
|
|
mEndElevation = 0.0f;
|
|
|
|
mLight = LightManager::createLightInfo();
|
|
mLight->setType( LightInfo::Vector );
|
|
|
|
mFlareData = NULL;
|
|
mFlareState.clear();
|
|
mFlareScale = 1.0f;
|
|
|
|
mCoronaEnabled = true;
|
|
mCoronaScale = 0.5f;
|
|
mCoronaTint.set( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
mCoronaUseLightColor = true;
|
|
mCoronaMatInst = NULL;
|
|
|
|
mMatrixSet = reinterpret_cast<MatrixSet *>(dMalloc_aligned(sizeof(MatrixSet), 16));
|
|
constructInPlace(mMatrixSet);
|
|
|
|
mCoronaWorldRadius = 0.0f;
|
|
mLightWorldPos = Point3F::Zero;
|
|
}
|
|
|
|
Sun::~Sun()
|
|
{
|
|
SAFE_DELETE( mLight );
|
|
SAFE_DELETE( mCoronaMatInst );
|
|
dFree_aligned(mMatrixSet);
|
|
}
|
|
|
|
bool Sun::onAdd()
|
|
{
|
|
if ( !Parent::onAdd() )
|
|
return false;
|
|
|
|
// Register as listener to TimeOfDay update events
|
|
TimeOfDay::getTimeOfDayUpdateSignal().notify( this, &Sun::_updateTimeOfDay );
|
|
|
|
// Make this thing have a global bounds so that its
|
|
// always returned from spatial light queries.
|
|
setGlobalBounds();
|
|
resetWorldBox();
|
|
setRenderTransform( mObjToWorld );
|
|
addToScene();
|
|
|
|
_initCorona();
|
|
|
|
// Update the light parameters.
|
|
_conformLights();
|
|
|
|
setProcessTick( true );
|
|
|
|
return true;
|
|
}
|
|
|
|
void Sun::onRemove()
|
|
{
|
|
TimeOfDay::getTimeOfDayUpdateSignal().remove( this, &Sun::_updateTimeOfDay );
|
|
|
|
removeFromScene();
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void Sun::initPersistFields()
|
|
{
|
|
addGroup( "Orbit" );
|
|
|
|
addField( "azimuth", TypeF32, Offset( mSunAzimuth, Sun ),
|
|
"The horizontal angle of the sun measured clockwise from the positive Y world axis." );
|
|
|
|
addField( "elevation", TypeF32, Offset( mSunElevation, Sun ),
|
|
"The elevation angle of the sun above or below the horizon." );
|
|
|
|
endGroup( "Orbit" );
|
|
|
|
// We only add the basic lighting options that all lighting
|
|
// systems would use... the specific lighting system options
|
|
// are injected at runtime by the lighting system itself.
|
|
|
|
addGroup( "Lighting" );
|
|
|
|
addField( "color", TypeColorF, Offset( mLightColor, Sun ),
|
|
"Color shading applied to surfaces in direct contact with light source.");
|
|
|
|
addField( "ambient", TypeColorF, Offset( mLightAmbient, Sun ), "Color shading applied to surfaces not "
|
|
"in direct contact with light source, such as in the shadows or interiors.");
|
|
|
|
addField( "brightness", TypeF32, Offset( mBrightness, Sun ),
|
|
"Adjust the Sun's global contrast/intensity");
|
|
|
|
addField( "castShadows", TypeBool, Offset( mCastShadows, Sun ),
|
|
"Enables/disables shadows cast by objects due to Sun light");
|
|
|
|
addField("staticRefreshFreq", TypeS32, Offset(mStaticRefreshFreq, Sun), "static shadow refresh rate (milliseconds)");
|
|
addField("dynamicRefreshFreq", TypeS32, Offset(mDynamicRefreshFreq, Sun), "dynamic shadow refresh rate (milliseconds)", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
|
|
|
|
endGroup( "Lighting" );
|
|
|
|
addGroup( "Corona" );
|
|
|
|
addField( "coronaEnabled", TypeBool, Offset( mCoronaEnabled, Sun ),
|
|
"Enable or disable rendering of the corona sprite." );
|
|
|
|
addField( "coronaMaterial", TypeMaterialName, Offset( mCoronaMatName, Sun ),
|
|
"Texture for the corona sprite." );
|
|
|
|
addField( "coronaScale", TypeF32, Offset( mCoronaScale, Sun ),
|
|
"Controls size the corona sprite renders, specified as a fractional amount of the screen height." );
|
|
|
|
addField( "coronaTint", TypeColorF, Offset( mCoronaTint, Sun ),
|
|
"Modulates the corona sprite color ( if coronaUseLightColor is false )." );
|
|
|
|
addField( "coronaUseLightColor", TypeBool, Offset( mCoronaUseLightColor, Sun ),
|
|
"Modulate the corona sprite color by the color of the light ( overrides coronaTint )." );
|
|
|
|
endGroup( "Corona" );
|
|
|
|
|
|
addGroup( "Misc" );
|
|
|
|
addField( "flareType", TYPEID< LightFlareData >(), Offset( mFlareData, Sun ),
|
|
"Datablock for the flare produced by the Sun" );
|
|
|
|
addField( "flareScale", TypeF32, Offset( mFlareScale, Sun ),
|
|
"Changes the size and intensity of the flare." );
|
|
|
|
endGroup( "Misc" );
|
|
|
|
// Now inject any light manager specific fields.
|
|
LightManager::initLightFields();
|
|
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
void Sun::inspectPostApply()
|
|
{
|
|
_conformLights();
|
|
setMaskBits(UpdateMask);
|
|
}
|
|
|
|
U32 Sun::packUpdate(NetConnection *conn, U32 mask, BitStream *stream )
|
|
{
|
|
U32 retMask = Parent::packUpdate( conn, mask, stream );
|
|
|
|
if ( stream->writeFlag( mask & UpdateMask ) )
|
|
{
|
|
stream->write( mSunAzimuth );
|
|
stream->write( mSunElevation );
|
|
stream->write( mLightColor );
|
|
stream->write( mLightAmbient );
|
|
stream->write( mBrightness );
|
|
stream->writeFlag( mCastShadows );
|
|
stream->write(mStaticRefreshFreq);
|
|
stream->write(mDynamicRefreshFreq);
|
|
stream->write( mFlareScale );
|
|
|
|
if ( stream->writeFlag( mFlareData ) )
|
|
{
|
|
stream->writeRangedU32( mFlareData->getId(),
|
|
DataBlockObjectIdFirst,
|
|
DataBlockObjectIdLast );
|
|
}
|
|
|
|
stream->writeFlag( mCoronaEnabled );
|
|
stream->write( mCoronaMatName );
|
|
stream->write( mCoronaScale );
|
|
stream->write( mCoronaTint );
|
|
stream->writeFlag( mCoronaUseLightColor );
|
|
|
|
mLight->packExtended( stream );
|
|
}
|
|
|
|
return retMask;
|
|
}
|
|
|
|
void Sun::unpackUpdate( NetConnection *conn, BitStream *stream )
|
|
{
|
|
Parent::unpackUpdate( conn, stream );
|
|
|
|
if ( stream->readFlag() ) // UpdateMask
|
|
{
|
|
stream->read( &mSunAzimuth );
|
|
stream->read( &mSunElevation );
|
|
stream->read( &mLightColor );
|
|
stream->read( &mLightAmbient );
|
|
stream->read( &mBrightness );
|
|
mCastShadows = stream->readFlag();
|
|
stream->read(&mStaticRefreshFreq);
|
|
stream->read(&mDynamicRefreshFreq);
|
|
stream->read( &mFlareScale );
|
|
|
|
if ( stream->readFlag() )
|
|
{
|
|
SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
|
LightFlareData *datablock = NULL;
|
|
|
|
if ( Sim::findObject( id, datablock ) )
|
|
mFlareData = datablock;
|
|
else
|
|
{
|
|
conn->setLastError( "Sun::unpackUpdate() - invalid LightFlareData!" );
|
|
mFlareData = NULL;
|
|
}
|
|
}
|
|
else
|
|
mFlareData = NULL;
|
|
|
|
mCoronaEnabled = stream->readFlag();
|
|
stream->read( &mCoronaMatName );
|
|
stream->read( &mCoronaScale );
|
|
stream->read( &mCoronaTint );
|
|
mCoronaUseLightColor = stream->readFlag();
|
|
|
|
mLight->unpackExtended( stream );
|
|
}
|
|
|
|
if ( isProperlyAdded() )
|
|
{
|
|
_initCorona();
|
|
_conformLights();
|
|
}
|
|
}
|
|
|
|
void Sun::submitLights( LightManager *lm, bool staticLighting )
|
|
{
|
|
// The sun is a special light and needs special registration.
|
|
lm->setSpecialLight( LightManager::slSunLightType, mLight );
|
|
}
|
|
|
|
|
|
void Sun::advanceTime( F32 timeDelta )
|
|
{
|
|
if (mAnimateSun)
|
|
{
|
|
if (mCurrTime >= mTotalTime)
|
|
{
|
|
mAnimateSun = false;
|
|
mCurrTime = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
mCurrTime += timeDelta;
|
|
|
|
F32 fract = mCurrTime / mTotalTime;
|
|
F32 inverse = 1.0f - fract;
|
|
|
|
F32 newAzimuth = mStartAzimuth * inverse + mEndAzimuth * fract;
|
|
F32 newElevation = mStartElevation * inverse + mEndElevation * fract;
|
|
|
|
if (newAzimuth > 360.0f)
|
|
newAzimuth -= 360.0f;
|
|
if (newElevation > 360.0f)
|
|
newElevation -= 360.0f;
|
|
|
|
setAzimuth(newAzimuth);
|
|
setElevation(newElevation);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Sun::prepRenderImage( SceneRenderState *state )
|
|
{
|
|
// Only render into diffuse and reflect passes.
|
|
|
|
if( !state->isDiffusePass() &&
|
|
!state->isReflectPass() )
|
|
return;
|
|
|
|
mLightWorldPos = state->getCameraPosition() - state->getFarPlane() * mLight->getDirection() * 0.9f;
|
|
F32 dist = ( mLightWorldPos - state->getCameraPosition() ).len();
|
|
|
|
F32 screenRadius = GFX->getViewport().extent.y * mCoronaScale * 0.5f;
|
|
mCoronaWorldRadius = screenRadius * dist / state->getWorldToScreenScale().y;
|
|
|
|
// Render instance for Corona effect.
|
|
if ( mCoronaEnabled && mCoronaMatInst )
|
|
{
|
|
mMatrixSet->setSceneProjection( GFX->getProjectionMatrix() );
|
|
mMatrixSet->setSceneView( GFX->getViewMatrix() );
|
|
mMatrixSet->setWorld( GFX->getWorldMatrix() );
|
|
|
|
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
|
ri->renderDelegate.bind( this, &Sun::_renderCorona );
|
|
ri->type = RenderPassManager::RIT_Sky;
|
|
// Render after sky objects and before CloudLayer!
|
|
ri->defaultKey = 5;
|
|
ri->defaultKey2 = 0;
|
|
state->getRenderPass()->addInst( ri );
|
|
}
|
|
|
|
// LightFlareData handles rendering flare effects.
|
|
if ( mFlareData )
|
|
{
|
|
mFlareState.fullBrightness = mBrightness;
|
|
mFlareState.scale = mFlareScale;
|
|
mFlareState.lightInfo = mLight;
|
|
mFlareState.worldRadius = mCoronaWorldRadius;
|
|
|
|
mFlareState.lightMat.identity();
|
|
mFlareState.lightMat.setPosition( mLightWorldPos );
|
|
|
|
mFlareData->prepRender( state, &mFlareState );
|
|
}
|
|
}
|
|
|
|
void Sun::setAzimuth( F32 azimuth )
|
|
{
|
|
mSunAzimuth = azimuth;
|
|
_conformLights();
|
|
setMaskBits( UpdateMask ); // TODO: Break out the masks to save bandwidth!
|
|
}
|
|
|
|
void Sun::setElevation( F32 elevation )
|
|
{
|
|
mSunElevation = elevation;
|
|
_conformLights();
|
|
setMaskBits( UpdateMask ); // TODO: Break out the masks to save some space!
|
|
}
|
|
|
|
void Sun::setColor( const LinearColorF &color )
|
|
{
|
|
mLightColor = color;
|
|
_conformLights();
|
|
setMaskBits( UpdateMask ); // TODO: Break out the masks to save some space!
|
|
}
|
|
|
|
void Sun::animate( F32 duration, F32 startAzimuth, F32 endAzimuth, F32 startElevation, F32 endElevation )
|
|
{
|
|
mAnimateSun = true;
|
|
mCurrTime = 0.0f;
|
|
|
|
mTotalTime = duration;
|
|
|
|
mStartAzimuth = startAzimuth;
|
|
mEndAzimuth = endAzimuth;
|
|
mStartElevation = startElevation;
|
|
mEndElevation = endElevation;
|
|
}
|
|
|
|
void Sun::_conformLights()
|
|
{
|
|
// Build the light direction from the azimuth and elevation.
|
|
F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359));
|
|
F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360));
|
|
VectorF lightDirection;
|
|
MathUtils::getVectorFromAngles(lightDirection, yaw, pitch);
|
|
lightDirection.normalize();
|
|
mLight->setDirection( -lightDirection );
|
|
mLight->setBrightness( mBrightness );
|
|
|
|
// Now make sure the colors are within range.
|
|
mLightColor.clamp();
|
|
mLight->setColor( mLightColor );
|
|
mLightAmbient.clamp();
|
|
mLight->setAmbient( mLightAmbient );
|
|
|
|
// Optimization... disable shadows if the ambient and
|
|
// directional color are the same.
|
|
bool castShadows = mLightColor != mLightAmbient && mCastShadows;
|
|
mLight->setCastShadows( castShadows );
|
|
mLight->setStaticRefreshFreq(mStaticRefreshFreq);
|
|
mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
|
|
}
|
|
|
|
void Sun::_initCorona()
|
|
{
|
|
if ( isServerObject() )
|
|
return;
|
|
|
|
SAFE_DELETE( mCoronaMatInst );
|
|
|
|
if ( mCoronaMatName.isNotEmpty() )
|
|
mCoronaMatInst = MATMGR->createMatInstance( mCoronaMatName, MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPCT>() );
|
|
}
|
|
|
|
void Sun::_renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
|
|
{
|
|
// Calculate Billboard Radius (in world units) to be constant, independent of distance.
|
|
// Takes into account distance, viewport size, and specified size in editor
|
|
F32 BBRadius = mCoronaWorldRadius;
|
|
|
|
mMatrixSet->restoreSceneViewProjection();
|
|
|
|
if ( state->isReflectPass() )
|
|
mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() );
|
|
|
|
//mMatrixSet->setWorld( MatrixF::Identity );
|
|
|
|
// Initialize points with basic info
|
|
Point3F points[4];
|
|
points[0] = Point3F(-BBRadius, 0.0, -BBRadius);
|
|
points[1] = Point3F( -BBRadius, 0.0, BBRadius);
|
|
points[2] = Point3F( BBRadius, 0.0, -BBRadius);
|
|
points[3] = Point3F(BBRadius, 0.0, BBRadius);
|
|
|
|
static const Point2F sCoords[4] =
|
|
{
|
|
Point2F( 0.0f, 0.0f ),
|
|
Point2F( 0.0f, 1.0f ),
|
|
Point2F( 1.0f, 0.0f ),
|
|
Point2F(1.0f, 1.0f)
|
|
};
|
|
|
|
// Get info we need to adjust points
|
|
const MatrixF &camView = state->getCameraTransform();
|
|
|
|
// Finalize points
|
|
for(S32 i = 0; i < 4; i++)
|
|
{
|
|
// align with camera
|
|
camView.mulV(points[i]);
|
|
// offset
|
|
points[i] += mLightWorldPos;
|
|
}
|
|
|
|
LinearColorF vertColor;
|
|
if ( mCoronaUseLightColor )
|
|
vertColor = mLightColor;
|
|
else
|
|
vertColor = mCoronaTint;
|
|
|
|
GFXVertexBufferHandle< GFXVertexPCT > vb;
|
|
vb.set( GFX, 4, GFXBufferTypeVolatile );
|
|
GFXVertexPCT *pVert = vb.lock();
|
|
if(!pVert) return;
|
|
|
|
for ( S32 i = 0; i < 4; i++ )
|
|
{
|
|
pVert->color.set( vertColor.toColorI());
|
|
pVert->point.set( points[i] );
|
|
pVert->texCoord.set( sCoords[i].x, sCoords[i].y );
|
|
pVert++;
|
|
}
|
|
|
|
vb.unlock();
|
|
|
|
// Setup SceneData struct.
|
|
|
|
SceneData sgData;
|
|
sgData.wireframe = GFXDevice::getWireframe();
|
|
sgData.visibility = 1.0f;
|
|
|
|
// Draw it
|
|
|
|
while ( mCoronaMatInst->setupPass( state, sgData ) )
|
|
{
|
|
mCoronaMatInst->setTransforms( *mMatrixSet, state );
|
|
mCoronaMatInst->setSceneInfo( state, sgData );
|
|
|
|
GFX->setVertexBuffer( vb );
|
|
GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
|
|
}
|
|
}
|
|
|
|
void Sun::_updateTimeOfDay( TimeOfDay *timeOfDay, F32 time )
|
|
{
|
|
setElevation( timeOfDay->getElevationDegrees() );
|
|
setAzimuth( timeOfDay->getAzimuthDegrees() );
|
|
}
|
|
|
|
void Sun::_onSelected()
|
|
{
|
|
#ifdef TORQUE_DEBUG
|
|
// Enable debug rendering on the light.
|
|
if( isClientObject() )
|
|
mLight->enableDebugRendering( true );
|
|
#endif
|
|
|
|
|
|
Parent::_onSelected();
|
|
}
|
|
|
|
void Sun::_onUnselected()
|
|
{
|
|
#ifdef TORQUE_DEBUG
|
|
// Disable debug rendering on the light.
|
|
if( isClientObject() )
|
|
mLight->enableDebugRendering( false );
|
|
#endif
|
|
|
|
Parent::_onUnselected();
|
|
}
|
|
|
|
DefineEngineMethod(Sun, apply, void, (), , "")
|
|
{
|
|
object->inspectPostApply();
|
|
}
|
|
|
|
DefineEngineMethod(Sun, animate, void, ( F32 duration, F32 startAzimuth, F32 endAzimuth, F32 startElevation, F32 endElevation ), , "animate( F32 duration, F32 startAzimuth, F32 endAzimuth, F32 startElevation, F32 endElevation )")
|
|
{
|
|
|
|
object->animate(duration, startAzimuth, endAzimuth, startElevation, endElevation);
|
|
}
|
|
|