|
|
|
/*==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==*/
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// hsVertexShader Class Functions //
|
|
|
|
// //
|
|
|
|
//// Version History /////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// 5.9.2001 mcn - Updated to reflect the new (temporary) vertex color/ //
|
|
|
|
// lighting model. //
|
|
|
|
// //
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include "hsBitVector.h"
|
|
|
|
#include "hsExceptionStack.h"
|
|
|
|
#include "hsMatrix44.h"
|
|
|
|
#include "hsTemplates.h"
|
|
|
|
#include "hsWindows.h"
|
|
|
|
|
|
|
|
#include <Max.h>
|
|
|
|
#include <stdmat.h>
|
|
|
|
#include <istdplug.h>
|
|
|
|
#include <dummy.h>
|
|
|
|
#include <notetrck.h>
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#include "MaxMain/plMaxNode.h"
|
|
|
|
#include "plSurface/hsGMaterial.h"
|
|
|
|
|
|
|
|
#include "UserPropMgr.h"
|
|
|
|
#include "hsMaxLayerBase.h"
|
|
|
|
#include "hsVertexShader.h"
|
|
|
|
|
|
|
|
#include "hsConverterUtils.h"
|
|
|
|
#include "hsControlConverter.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;
|
|
|
|
bool translucent, shadeIt, addingIt;
|
|
|
|
plLayerInterface *layer = nil;
|
|
|
|
|
|
|
|
|
|
|
|
hsGuardBegin("hsVertexShader::ShadeSpan");
|
|
|
|
|
|
|
|
const char* dbgNodeName = node->GetName();
|
|
|
|
|
|
|
|
if( span->fNumVerts == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
fShadeColorTable = new hsColorRGBA[ span->fNumVerts ];
|
|
|
|
fIllumColorTable = 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, bool 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 = 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, bool 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, bool translucent)
|
|
|
|
{
|
|
|
|
ctx.SetPoint(vtx.fLocalPos, vtx.fNormal);
|
|
|
|
|
|
|
|
Color color = fLightMapGen->ShadowPoint(ctx);
|
|
|
|
shade.r += color.r;
|
|
|
|
shade.g += color.g;
|
|
|
|
shade.b += color.b;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|