⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wmlbumpmap.cpp

📁 3D Game Engine Design Source Code非常棒
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// Magic Software, Inc.
// http://www.magic-software.com
// http://www.wild-magic.com
// Copyright (c) 2003.  All Rights Reserved
//
// The Wild Magic Library (WML) source code is supplied under the terms of
// the license agreement http://www.magic-software.com/License/WildMagic.pdf
// and may not be copied or disclosed except in accordance with the terms of
// that agreement.

#include "WmlBumpMap.h"
#include "WmlDirectionalLight.h"
#include "WmlPointLight.h"
#include "WmlRenderer.h"
#include "WmlTriMesh.h"
using namespace Wml;

WmlImplementRTTI(BumpMap,Node);
WmlImplementStream(BumpMap);

//----------------------------------------------------------------------------
BumpMap::BumpMap (Node* pkObjects, Texture* pkNormalMap, Light* pkLight,
    bool bModulate)
    :
    m_spkObjects(pkObjects),
    m_spkNormalMap(pkNormalMap), 
    m_spkLight(pkLight)
{
    assert( pkObjects && pkNormalMap && pkLight );

    m_bModulate = bModulate;

    // Save the cull state of the objects.  These objects will be force-culled
    // by 'this' to make sure they are not drawn by the regular traversal.
    m_bForceCullObjects = pkObjects->ForceCull();
    pkObjects->ForceCull() = true;
    
    // create texture states
    m_spkTextureState = new TextureState;
    m_spkTextureStateModulated = new TextureState;

    // set apply mode for normal map
    m_spkNormalMap->Apply() = Texture::AM_COMBINE;
    m_spkNormalMap->CombineFuncRGB() = Texture::ACF_DOT3_RGB;
    m_spkNormalMap->CombineSrc0RGB() = Texture::ACS_TEXTURE;
    m_spkNormalMap->CombineOp0RGB() = Texture::ACO_SRC_COLOR;
    m_spkNormalMap->CombineSrc1RGB() = Texture::ACS_PRIMARY_COLOR;
    m_spkNormalMap->CombineOp1RGB() = Texture::ACO_SRC_COLOR;

    // set up the modulated and unmodulated texture states
    m_spkTextureState->Set(0,m_spkNormalMap);
    m_spkTextureStateModulated->Set(0,m_spkNormalMap);
    
    // The blend color can be modified by the application using the function
    // SetCurrentDiffuseMaterial.
    Texture* pkTexture = new Texture;
    pkTexture->Apply() = Texture::AM_COMBINE;
    pkTexture->CombineFuncRGB() = Texture::ACF_MODULATE;
    pkTexture->CombineSrc0RGB() = Texture::ACS_CONSTANT;
    pkTexture->CombineOp0RGB() = Texture::ACO_SRC_COLOR;
    pkTexture->CombineSrc1RGB() = Texture::ACS_PREVIOUS;
    pkTexture->CombineOp1RGB() = Texture::ACO_SRC_COLOR;
    pkTexture->BlendColor() = pkLight->Diffuse();
    m_spkTextureStateModulated->Set(1,pkTexture);

    // The blend color can be modified by the application using the function
    // SetCurrentAmbientMaterial.
    pkTexture = new Texture;
    pkTexture->Apply() = Texture::AM_COMBINE;
    pkTexture->CombineFuncRGB() = Texture::ACF_ADD;
    pkTexture->CombineSrc0RGB() = Texture::ACS_PREVIOUS;
    pkTexture->CombineOp0RGB() = Texture::ACO_SRC_COLOR;
    pkTexture->CombineSrc1RGB() = Texture::ACS_CONSTANT;
    pkTexture->CombineOp1RGB() = Texture::ACO_SRC_COLOR;
    pkTexture->BlendColor() = pkLight->Ambient();
    m_spkTextureStateModulated->Set(2,pkTexture);

    // create an alpha blending state
    m_spkAlphaState = new AlphaState;
    m_spkAlphaState->BlendEnabled() = true;
    m_spkAlphaState->TestEnabled() = false;
    m_spkAlphaState->SrcBlend() = AlphaState::SBF_DST_COLOR;
    m_spkAlphaState->DstBlend() = AlphaState::DBF_ZERO;
}
//----------------------------------------------------------------------------
BumpMap::BumpMap ()
{
    m_bModulate = false;
    m_bForceCullObjects = false;
}
//----------------------------------------------------------------------------
BumpMap::~BumpMap()
{
    // restore the cull state
    m_spkObjects->ForceCull() = m_bForceCullObjects;

    // release the render effect objects
    m_spkObjects = NULL;
    m_spkNormalMap = NULL;
    m_spkTextureState = NULL;
    m_spkTextureStateModulated = NULL;
    m_spkAlphaState = NULL;
    m_spkLight = NULL;
}
//----------------------------------------------------------------------------
void BumpMap::ComputeLightVectors (TriMesh& rkMesh)
{
    // Generate light direction vectors in the surface local space and store
    // them in the trimesh for interpolation via the rasterizer.  This assumes
    // the user has provided normalized normals.
    if ( !rkMesh.Normals() || !rkMesh.Textures() )
        return;

    assert( rkMesh.Colors() );

    // get the world light vector
    Vector3f kWLight;
    switch ( m_spkLight->GetType() )
    {
    case Light::LT_DIRECTIONAL:
        kWLight = -WmlStaticCast(DirectionalLight,m_spkLight)->Direction();
        break;
    case Light::LT_POINT:
    case Light::LT_SPOT:
        kWLight = WmlStaticCast(PointLight,m_spkLight)->Location();
        break;
    default:
        // ambient light, nothing we can do to handle this
        return;
    }

    // transform the world light vector into model space
    Vector3f kMLight = kWLight - rkMesh.WorldTranslate();
    kMLight = kMLight*rkMesh.WorldRotate();
    kMLight /= rkMesh.WorldScale();

    // The surface local space information is computed for all the vertices.
    // We iterate over the triangles because we need to know the connectivity
    // information.  The end result is assignment of the light vectors.
    Vector3f* akVertex = rkMesh.Vertices();
    Vector3f* akNormal = rkMesh.Normals();
    Vector2f* akUV = rkMesh.Textures();

    // Set the light vectors to (0,0,0) as a flag that the quantity has not
    // yet been computed.  The probability that a light vector is actually
    // (0,0,0) should be small, so the flag system should save computation
    // time overall.
    ColorRGB* akLVec = rkMesh.Colors();
    memset(akLVec,0,rkMesh.GetVertexQuantity()*sizeof(ColorRGB));

    for (int iT = 0; iT < rkMesh.GetTriangleQuantity(); iT++)
    {
        // get the triangle vertices and attributes
        int aiIndex[3];
        rkMesh.GetTriangle(iT,aiIndex[0],aiIndex[1],aiIndex[2]);
        
        Vector3f* apkV[3] =
        {
            &akVertex[aiIndex[0]],
            &akVertex[aiIndex[1]],
            &akVertex[aiIndex[2]]
        };

        Vector3f* apkN[3] =
        {
            &akNormal[aiIndex[0]],
            &akNormal[aiIndex[1]],
            &akNormal[aiIndex[2]]
        };

        Vector2f* apkUV[3] =
        {
            &akUV[aiIndex[0]],
            &akUV[aiIndex[1]],
            &akUV[aiIndex[2]]
        };

        for (int i = 0; i < 3; i++)
        {
            ColorRGB& rkColor = akLVec[aiIndex[i]];
            if ( rkColor != ColorRGB::BLACK )
                continue;

            // Compute the surface local space at each vertex.
            //
            // TO DO.  If the geometry is static in model space, then we
            // should precompute the surface local space and store it.
            //
            // The normal vector N is the surface normal.  The vertex normal
            // is used as an approximation to N.
            //
            // The tangent vector T is computed by thinking of the surface in
            // parametric form P(u,v) for some scalar variables u and v.  In
            // this case, a tangent is the partial derivative T = dP/du.  This
            // quantity is estimated from the triangle attributes.  The
            // texture coordinates (u,v) are used as an approximation to the
            // parametric quantities.
            //
            // The binormal vector is B = Cross(N,T).  If the estimation of
            // T is ill-posed, we try BLAH BLAH BLAH.
            
            //The tangent T is defined
            // by dSurf/dS, and the binormal B = Cross(N,T).  We assume the
            // normals are normalized,  and we assume the model is
            // parametrized so that the square patch assumption holds.
            //
            // We need to compute the tangent vector at the current vertex.
            // If we think of the surface as being a vector-valued function
            // P(u,v), then we want to set T to be dP/du.  We try to compute
            // T, but if the parametrization is such that our direct
            // definition of T is not applicable, then we try for the binormal
            // directly and then back out a reasonable T.  If there is no
            // useful parametrization information, we just assume that the
            // light vector is in the same direction as the surface normal.

            int iP = (i == 0) ? 2 : i - 1;
            int iN = (i + 1) % 3;

            // compute edge vectors at the specified vertex
            Vector3f kDP1 = *apkV[iN] - *apkV[i];
            Vector3f kDP2 = *apkV[iP] - *apkV[i];

            // estimate a tangent surface vector
            Vector3f kTangent;
            bool fDegenerate = false;
            const float fEpsilon = 1e-05f;

            if ( Mathf::FAbs(kDP1.Length()) < fEpsilon
            ||   Mathf::FAbs(kDP2.Length()) < fEpsilon )
            {
                // The triangle is very small, call it degenerate.
                fDegenerate = true;
            }
            else
            {
                // difference of surface parameters along triangle edge
                float fDU1 = apkUV[iN]->X() - apkUV[i]->X();
                float fDV1 = apkUV[iN]->Y() - apkUV[i]->Y();

                if ( Mathf::FAbs(fDV1) < fEpsilon )
                {
                    // The triangle effectively has no variation in the v
                    // texture coordinate.
                    if ( Mathf::FAbs(fDU1) < fEpsilon )
                    {
                        // The triangle effectively has no variation in the u
                        // coordinate.  Since the texture coordinates do not
                        // effectively vary on this triangle, treat it as a
                        // degenerate parametric surface.
                        fDegenerate = true;
                    }
                    else
                    {
                        // The variation is effectively all in u, so set the
                        // tangent T = dP/du.
                        kTangent = kDP1/fDU1;
                    }
                }
                else
                {
                    // difference of surface parameters along triangle edge
                    float fDU2 = apkUV[iP]->X() - apkUV[i]->X();
                    float fDV2 = apkUV[iP]->Y() - apkUV[i]->Y();
                    float fDet = fDV1*fDU2 - fDV2*fDU1;

                    if ( Mathf::FAbs(fDet) >= fEpsilon )
                    {
                        // The triangle vertices form three collinear points
                        // in parameter space, so
                        //   dP/du = (dv1*dP2-dv2*dP1)/(dv1*du2-dv2*du1)
                        kTangent = (fDV1*kDP2-fDV2*kDP1)/fDet;
                    }
                    else
                    {
                        // The triangle vertices are collinear in parameter
                        // space.
                        fDegenerate = true;
                    }
                }
            }

            if ( fDegenerate )
            {
                // The texture coordinate mapping is not properly defined for
                // this.  Just say that the tangent space light vector points
                // in the same direction as the surface normal.
                rkColor.r = 0.0f;
                rkColor.g = 0.0f;
                rkColor.b = 1.0f;
                continue;
            }

            // Project T into the tangent plane by projecting out the surface
            // normal, then make it unit length.
            kTangent -= apkN[i]->Dot(kTangent)*(*apkN[i]);
            kTangent.Normalize();

            // compute the binormal B, another tangent perpendicular to T
            Vector3f kBinormal = apkN[i]->UnitCross(kTangent);

            // When generating bump/normal maps, folks usually work in a
            // left-handed screen space with the origin at the upper right,
            // u to the right, and v down, while we apply the textures with
            // the origin at the lower left, u right, v up, so we need to flip
            // the binormal (v-axis) to get a proper transformation to the
            // surface local texture space.
            Matrix3f kRotTS(
                kTangent.X(),   kTangent.Y(),   kTangent.Z(),

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -