TGB/Resources/Dynamic Shadow

From TDN

Down below, you'll find t2dLightMap.h and t2dLightMap.cc. You'll need to create these in your project and then copy the code into them.

Here's the TorqueScript to add to the scene:

%lightMap = new t2dLightMap() {
  scenegraph = <YOUR SCENE GRAPH>;
  Layer = 8;
  size = "1250 1250";
};
%lightMap.mount( ArcadeGrommy, 0, 0, 0, false, true, true, false );

In this case, I've made a rather large area that attaches to the main player in the center of the screen. I put the shadows on layer 8 so that I could have "emissive" objects above it and hidden objects below it.

There are notes in t2dLightMap.cc as to the things you'll have to change. This is really rough, so ask in the forums if you have any questions or need something clarified.

t2dLightMap.h

#ifndef _t2dLightMap_H_
#define _t2dLightMap_H_

#ifndef _T2DSCENEOBJECT_H_
#include "T2D/t2dSceneObject.h"
#endif

#ifndef _T2DIMAGEMAPDATABLOCK_H_
#include "T2D/t2dImageMapDatablock.h"
#endif

class t2dLightMap : public t2dSceneObject
{
  typedef t2dSceneObject          Parent;

public:
  t2dLightMap();
  virtual ~t2dLightMap();

  static void initPersistFields();
  
  virtual bool onAdd();
  virtual void onRemove();

  virtual void renderObject( const RectF& viewPort, const RectF& viewIntersection );
  void processObject( t2dSceneObject *obj );
  void renderShadow( const Vector<t2dVector>& verts, const t2dVector& lightPos );

  DECLARE_CONOBJECT( t2dLightMap );

protected:
  
private:
  F32 theSinTable[361];
  F32 theCosTable[361];
};

#endif // _t2dLightMap_H_

t2dLightMap.cc

#include "t2dLightMap.h"

#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "core/bitStream.h"
#include "core/fileObject.h"

IMPLEMENT_CONOBJECT(t2dLightMap);

t2dLightMap::t2dLightMap()
{
  for( int i = 0; i <= 360; ++i )
  {
    theSinTable[i] = mSin( mDegToRad( (F32)i ) );
    theCosTable[i] = mCos( mDegToRad( (F32)i ) );
  }
}

t2dLightMap::~t2dLightMap()
{
}

void t2dLightMap::initPersistFields()
{
  Parent::initPersistFields();
}

bool t2dLightMap::onAdd()
{
  if(!Parent::onAdd())
    return false;

  return true;
}

void t2dLightMap::onRemove()
{
  Parent::onRemove();
}

void t2dLightMap::renderObject( const RectF& viewPort, const RectF& viewIntersection )
{
  t2dVector worldPos = getPosition();
  F32 radius = getSize().mX * 0.5;

  glPushMatrix();
  glTranslatef( worldPos.mX, worldPos.mY, 0 );
  glPolygonMode( GL_FRONT, GL_FILL );

  glBlendFunc( GL_DST_COLOR, GL_ZERO );

  // Creates the fading dark region.
  glBegin(GL_TRIANGLE_FAN);
  glColor4f( 1, 1, 1, 1 );
  glVertex2f( 0, 0 );
  glColor4f( 0, 0, 0, 1 );
  for( int i = 0; i <= 36; ++i )
  {
    F32 cx = radius * theCosTable[10*i];
    F32 cy = radius * theSinTable[10*i];
    glVertex2f( cx, cy );
  }
  glEnd();

  // Creates the total dark region outside of the radius
  glBegin(GL_TRIANGLE_STRIP);
  glColor4f( 0, 0, 0, 1 );
  for( int i = 0; i <= 36; ++i )
  {
    F32 ix = radius * theCosTable[10*i];
    F32 iy = radius * theSinTable[10*i];
    F32 ox = ix * 1.75;
    F32 oy = iy * 1.75;
    glVertex2f( ix, iy );
    glVertex2f( ox, oy );
  }
  glEnd();

  glPopMatrix();
  
  // Limits search to a SPECIFIC t2dSceneWindow and t2dSceneGraph.
  // NOTE!  You must change this to name your window and scene!
  t2dSceneWindow *arcadeWindow = dynamic_cast<t2dSceneWindow *>(Sim::findObject( "arcadeWindow" ));
  t2dSceneGraph *arcadeScene = dynamic_cast<t2dSceneGraph *>(Sim::findObject( "ArcadeScene" ));
  if( !arcadeWindow || !arcadeScene ) return;

  RectF camArea = arcadeWindow->getCurrentCameraArea();
  t2dVector ul( camArea.point.x, camArea.point.y );
  t2dVector lr( camArea.point.x + camArea.extent.x, camArea.point.y + camArea.extent.y );
  // NOTE!  I only care about the objects in GROUP #2.  You must change this!
  S32 matches = arcadeScene->pickRect( ul, lr, BIT(2) );
  if( matches == 0 ) return;

  typeSceneObjectVectorConstRef pickVector = arcadeScene->getPickList();

  glBlendFunc( GL_DST_COLOR, GL_ZERO );

  for( S32 i = 0; i < matches; ++i )
  {
    processObject( pickVector[i] );
  }

  Parent::renderObject( viewPort, viewIntersection ); // Always use for Debug Support!
}

void t2dLightMap::processObject( t2dSceneObject *obj )
{
  // This is the hard section.  In my game, all of the objects passed into
  // this are my pillars.  This creates a set of vertices that define the
  // pillars, and then those vertices are passed to the shadow generating
  // function "renderShadow".
  //
  // You need to come up with your own model.  You might provide a collision
  // list on your objects, which you could then process here into a vertex
  // list.  You might do like I did and say that all of your objects are of
  // a single type and that you'll create the vertex list dynamically.

  t2dVector lightPos = getPosition();

  t2dVector objSize = obj->getSize();
  t2dVector objPos = obj->getPosition();
  F32 objRot = obj->getRotation();

  Vector<t2dVector> verts;
  for( int i = 0; i < 24; ++i )
  {
    t2dVector a( 60 * theCosTable[15*i], 60 * theSinTable[15*i] );
    a += objPos;
    verts.push_back( a );
  }
  renderShadow( verts, lightPos );
}

void t2dLightMap::renderShadow( const Vector<t2dVector>& verts, const t2dVector& lightPos )
{
  S32 startingVert = -999, endingVert = -999;
  for( S32 i = 1; i < verts.size() + 1; ++i )
  {
    S32 vert0 = (i-1) % verts.size();
    S32 vert1 = (i) % verts.size();
    S32 vert2 = (i+1) % verts.size();
    t2dVector normal0( verts[vert0] - verts[vert1] ); normal0.perp();
    t2dVector normal1( verts[vert1] - verts[vert2] ); normal1.perp();
    t2dVector ray( verts[vert1] - lightPos );
    F32 n0 = ray.dot( normal0 );
    F32 n1 = ray.dot( normal1 );
    if( n0 <= 0 && n1 > 0 )
    {
      startingVert = vert1;
    }
    if( n0 > 0 && n1 <= 0 )
    {
      endingVert = vert1;
    }
  }

  glBegin( GL_TRIANGLE_STRIP );
  glColor4f( 0, 0, 0, 1 );
  for( S32 i = 0; i < verts.size(); ++i )
  {
    S32 vert = (startingVert + i) % verts.size();
    
    t2dVector ray( verts[vert] - lightPos ); ray.normalise( 600 );
    glVertex2f( verts[vert].mX, verts[vert].mY );
    glVertex2f( verts[vert].mX + ray.mX, verts[vert].mY + ray.mY );

    if( vert == endingVert ) break;
  }
  glEnd();
}