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.

442 lines
14 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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// hsVertexShader Class Functions //
// //
//// Version History /////////////////////////////////////////////////////////
// //
// 5.9.2001 mcn - Updated to reflect the new (temporary) vertex color/ //
// lighting model. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "HeadSpin.h"
#include "Max.h"
#include "stdmat.h"
#include "istdplug.h"
#include "dummy.h"
#include "notetrck.h"
#include "../MaxMain/plMaxNode.h"
#include "hsBitVector.h"
#include "hsMatrix44.h"
#include "hsTemplates.h"
#include "../plSurface/hsGMaterial.h"
#include "UserPropMgr.h"
#include "hsMaxLayerBase.h"
#include "hsVertexShader.h"
#include "hsConverterUtils.h"
#include "hsControlConverter.h"
#include "hsExceptionStack.h"
#include "../plSurface/hsGMaterial.h"
#include "../plSurface/plLayerInterface.h"
#include "../plDrawable/plGeometrySpan.h"
#include "plMaxLightContext.h"
#include "plRenderGlobalContext.h"
#include "plLightMapGen.h"
#define MF_NATIVE_MAX_LIGHT
#define MF_NO_RAY_SHADOW
extern UserPropMgr gUserPropMgr;
extern TimeValue GetTime(Interface *gi);
//===========================================================================
// hsVertexShader
//===========================================================================
hsVertexShader::hsVertexShader() :
fConverterUtils(hsConverterUtils::Instance()),
fInterface(nil),
fLightMapGen(nil),
fShaded(0)
{
hsGuardBegin("hsVertexShader::hsVertexShader");
fLocalToWorld.Reset();
hsGuardEnd;
}
hsVertexShader::~hsVertexShader()
{
hsGuardBegin("hsVertexShader::~hsVertexShader");
hsGuardEnd;
}
hsVertexShader& hsVertexShader::Instance()
{
hsGuardBegin("hsVertexShader::Instance");
static hsVertexShader instance;
return instance;
hsGuardEnd;
}
void hsVertexShader::Open()
{
hsGuardBegin("hsVertexShader::InitLights");
fLocalToWorld.Reset();
fInterface = ::GetCOREInterface();
fLightMapGen = &plLightMapGen::Instance();
hsGuardEnd;
}
void hsVertexShader::Close()
{
hsGuardBegin("hsVertexShader::DeInitLights");
fLightMapGen = nil;
hsGuardEnd;
}
//// ShadeNode ///////////////////////////////////////////////////////////////
// Same as the other ShadeNode, only this shades an array of plGeometrySpans.
void hsVertexShader::ShadeNode(INode* node, hsMatrix44& l2w, hsMatrix44& w2l, hsTArray<plGeometrySpan *> &spans)
{
// If we're flagged for WaterColor, our vertex colors are already done.
if( ((plMaxNodeBase*)node)->GetCalcEdgeLens() || node->UserPropExists("XXXWaterColor") )
return;
fLightMapGen->InitNode(node);
fLocalToWorld = l2w;
hsMatrix44 tempMatrix = w2l; // l2w's inverse
tempMatrix.GetTranspose( &fNormalToWorld ); // Inverse-transpose of the fLocalToWorld matrix,
int i;
for( i = 0; i < spans.GetCount(); i++ )
IShadeSpan( spans[ i ], node);
fLightMapGen->DeInitNode();
fShaded++;
}
//// IShadeSpan //////////////////////////////////////////////////////////////
// Shades a single plGeometrySpan.
// 5.9.2001 mcn - Updated to support the new (temporary) vertex color/lighting
// method.
void hsVertexShader::IShadeSpan( plGeometrySpan *span, INode* node )
{
hsColorRGBA preDiffuse, rtDiffuse, matAmbient;
hsBitVector dirtyVector;
int i;
hsBool translucent, shadeIt, addingIt;
plLayerInterface *layer = nil;
hsGuardBegin("hsVertexShader::ShadeSpan");
const char* dbgNodeName = node->GetName();
if( span->fNumVerts == 0 )
return;
fShadeColorTable = TRACKED_NEW hsColorRGBA[ span->fNumVerts ];
fIllumColorTable = TRACKED_NEW hsColorRGBA[ span->fNumVerts ];
translucent = IsTranslucent( span->fMaterial );
/// Get material layer #0
addingIt = false;
shadeIt = !( span->fProps & plGeometrySpan::kPropNoPreShade );
if( span->fMaterial->GetNumLayers() != 0 )
{
layer = span->fMaterial->GetLayer( 0 );
if( layer->GetShadeFlags() & hsGMatState::kShadeNoShade )
shadeIt = false;
if( layer->GetBlendFlags() & hsGMatState::kBlendAdd )
addingIt = true;
}
float opacity = 1.f;
for( i = 0; i < span->fMaterial->GetNumLayers(); i++ )
{
plLayerInterface* lay = span->fMaterial->GetLayer(i);
if( (lay->GetBlendFlags() & hsGMatState::kBlendAlpha)
&&
(
!i
||
(lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere)
)
)
{
opacity = span->fMaterial->GetLayer(i)->GetOpacity();
}
}
/// Generate color table
if( shadeIt )
IShadeVertices( span, &dirtyVector, node, translucent );
else
{
for( i = 0; i < span->fNumVerts; i++ )
{
/// This is good for the old way, but not sure about the new way. Test once new way is in again -mcn
// fShadeColorTable[ i ].Set( 1, 1, 1, 1 );
// fIllumColorTable[ i ].Set( 0, 0, 0, 1 );
hsPoint3 position;
hsVector3 normal;
hsColorRGBA color, illum;
span->ExtractVertex( i, &position, &normal, &color, &illum );
span->ExtractInitColor( i, &color, &illum );
fShadeColorTable[ i ].Set( color.r, color.g, color.b, color.a );
fIllumColorTable[ i ].Set( illum.r, illum.g, illum.b, 1 );
}
}
/// Get mat colors to modulate by
if( layer == nil )
{
preDiffuse.Set( 1, 1, 1, 1 );
rtDiffuse.Set( 1, 1, 1, 1 );
matAmbient.Set( 0, 0, 0, 0 );
}
else
{
if( layer->GetShadeFlags() & hsGMatState::kShadeWhite )
{
preDiffuse.Set( 1, 1, 1, 1 );
rtDiffuse.Set( 1, 1, 1, 1 );
matAmbient.Set( 0, 0, 0, 0 );
}
else
{
preDiffuse = layer->GetPreshadeColor(); // This is for vertex-based lighting, which basically ignores preshading
rtDiffuse = layer->GetRuntimeColor(); // This is for vertex-based lighting, which basically ignores preshading
matAmbient = layer->GetAmbientColor();
matAmbient.a = 0;
}
preDiffuse.a = opacity;
rtDiffuse.a = opacity;
}
#if 0
/// Multiply by the material color, and scale by opacity if we're additive blending
/// Apply colors now, multiplying by the material color as we go
for( i = 0; i < span->fNumVerts; i++ )
{
fShadeColorTable[ i ] *= matDiffuse;
fShadeColorTable[ i ] += matAmbient;
fIllumColorTable[ i ] *= matDiffuse;
fIllumColorTable[ i ] += matAmbient;
}
if( addingIt )
{
for( i = 0; i < span->fNumVerts; i++ )
{
float opacity = fShadeColorTable[ i ].a;
fShadeColorTable[ i ] *= opacity;
fIllumColorTable[ i ] *= opacity;
}
}
#else
/// Combine shade and illum together into the diffuse color
if( ( span->fProps & plGeometrySpan::kLiteMask ) != plGeometrySpan::kLiteMaterial )
{
/// The two vertex lighting formulas take in a vetex color pre-processed, i.e. in
/// the form of: vtxColor = ( maxVtxColor * materialDiffuse + maxIllumColor )
span->fProps |= plGeometrySpan::kDiffuseFoldedIn;
if( !shadeIt )
{
for( i = 0; i < span->fNumVerts; i++ )
{
fIllumColorTable[ i ].a = 0;
fShadeColorTable[ i ] = (fShadeColorTable[ i ] * rtDiffuse) + fIllumColorTable[ i ];
fIllumColorTable[ i ].Set( 0, 0, 0, 0 );
}
}
else
{
for( i = 0; i < span->fNumVerts; i++ )
{
fIllumColorTable[ i ].a = 1.f;
// Following needs to be changed to allow user input vertex colors to modulate
// the runtime light values.
// fShadeColorTable[ i ] = fIllumColorTable[ i ] * rtDiffuse;
fShadeColorTable[ i ] = fShadeColorTable[ i ] * fIllumColorTable[ i ] * rtDiffuse;
fIllumColorTable[ i ].Set( 0, 0, 0, 0 );
}
}
}
else
{
if( !shadeIt )
{
// Not shaded, so runtime lit, so we want BLACK vertex colors
for( i = 0; i < span->fNumVerts; i++ )
{
fShadeColorTable[ i ].Set( 0, 0, 0, 0 );
fIllumColorTable[ i ].Set( 0, 0, 0, 0 );
}
}
else
{
for( i = 0; i < span->fNumVerts; i++ )
{
fShadeColorTable[ i ] *= fIllumColorTable[ i ];
fIllumColorTable[ i ].Set( 0, 0, 0, 0 );
}
}
}
#endif
/// Loop and stuff
for( i = 0; i < span->fNumVerts; i++ )
span->StuffVertex( i, fShadeColorTable + i, fIllumColorTable + i );
delete [] fShadeColorTable;
delete [] fIllumColorTable;
hsGuardEnd;
}
//// IShadeVertices //////////////////////////////////////////////////////////
// Shades an array of vertices from a plGeometrySpan.
// 5.9.2001 mcn - Updated for the new lighting model. Now on runtime, we
// want the following properties on each vertex:
// diffuseColor = vertexColor * matDiffuse + matAmbient (including alpha)
// specularColor = ( illumniation + pre-shading ) * matDiffuse + matAmbient
// We do the mat modulation outside of this function, so we
// just gotta make sure the two arrays get the right values.
void hsVertexShader::IShadeVertices( plGeometrySpan *span, hsBitVector *dirtyVector, INode* node, hsBool translucent )
{
hsGuardBegin( "hsVertexShader::IShadeVertices" );
plMaxNode* maxNode = (plMaxNode*)node;
if( maxNode->CanConvert() && (nil != maxNode->GetLightMapComponent()) )
return;
int index;
hsPoint3 position;
hsVector3 normal;
hsColorRGBA color, illum;
plTmpVertex3 *vertices;
/// Allocate temp vertex array
vertices = TRACKED_NEW plTmpVertex3[ span->fNumVerts ];
for( index = 0; index < span->fNumVerts; index++ )
{
span->ExtractVertex( index, &position, &normal, &color, &illum );
span->ExtractInitColor( index, &color, &illum );
/// fShadeColorTable is the shaded portion. fIllumColorTable is the illuminated portion;
/// for more and less confusing details, see above.
fShadeColorTable[ index ].Set( color.r, color.g, color.b, color.a );
fIllumColorTable[ index ].Set( illum.r, illum.g, illum.b, 1 );
position = fLocalToWorld * position;
normal = fNormalToWorld * normal;
vertices[ index ].fLocalPos = position;
vertices[ index ].fNormal = normal;
vertices[ index ].fNormal.Normalize();
}
const char* dbgNodeName = node->GetName();
TimeValue t = fInterface->GetTime();
Box3 bbox;
node->EvalWorldState(t).obj->GetDeformBBox(t, bbox, &node->GetObjectTM(t));
plMaxLightContext ctx(bbox, t);
for( index = 0; index < span->fNumVerts; index++ )
INativeShadeVtx(fIllumColorTable[index], ctx, vertices[ index ], translucent);
// Delete temp arrays
delete [] vertices;
hsGuardEnd;
}
void hsVertexShader::INativeShadeVtx(hsColorRGBA& shade, plMaxLightContext& ctx, const plTmpVertex3& vtx, hsBool translucent)
{
ctx.SetPoint(vtx.fLocalPos, vtx.fNormal);
Color color = fLightMapGen->ShadePoint(ctx);
shade.r += color.r;
shade.g += color.g;
shade.b += color.b;
// To handle two-sided translucency here, we should compute the shade on each side and sum them.
if( translucent )
{
ctx.SetPoint(vtx.fLocalPos, -vtx.fNormal);
color = fLightMapGen->ShadePoint(ctx);
shade.r += color.r;
shade.g += color.g;
shade.b += color.b;
}
}
void hsVertexShader::INativeShadowVtx(hsColorRGBA& shade, plMaxLightContext& ctx, const plTmpVertex3& vtx, hsBool translucent)
{
ctx.SetPoint(vtx.fLocalPos, vtx.fNormal);
Color color = fLightMapGen->ShadowPoint(ctx);
shade.r += color.r;
shade.g += color.g;
shade.b += color.b;
}
hsBool hsVertexShader::IsTranslucent( hsGMaterial *material )
{
hsGuardBegin("hsVertexShader::IsTranslucent");
if( material )
{
plLayerInterface* layer = material->GetLayer(0);
if( layer && ( layer->GetShadeFlags() & hsGMatState::kShadeSoftShadow ) )
{
return true;
}
}
return false;
hsGuardEnd;
}