/*==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 . 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 "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 &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; }