Game Development Community

Issue "Wrapping" Decals

by DavidRM · in Torque Game Engine · 05/03/2006 (3:48 pm) · 7 replies

I'm trying to extend decals so that they will "wrap" around/across terrain and interiors, instead of just being a simple plane with a bitmap.

I pulled apart the shadow rendering to see how that was done, and lifted a chunk of the code. It almost works...

Sometimes, across simple ridges, it works perfectly. Around/across more complex angles, parts of the decal aren't visible. Sometimes only a portion of the decal (usually a vertical strip) is visible. (see images; I made the decal much bigger to better demonstrate the problem)

www.ascendantmoon.com/images/decal-problem-example-01.jpg
www.ascendantmoon.com/images/decal-problem-example-02.jpg
www.ascendantmoon.com/images/decal-problem-example-03.jpg
Here's the core of the code that calculates the decal "overlay":

...updated code in next post

I suspect the problem is in the transform matrix or in the box/sphere calculations. But my 3D math is rusty at best (this spring marks 16 years since Linear Algebra), so I'm not sure what to adjust.

Any assitance would be appreciated.

Thanks.

-David

#1
05/04/2006 (1:12 pm)
I cleaned up the code some today and it's mostly working now. Still has issues with some surfaces (especially floors and ceilings), and with right-angle shapes.

...updated code in next post

I'd love to find out what I'm doing wrong in the case of floors, ceilings, and why sometimes the decal is clipped oddly. Any suggestions?

-David
#2
05/04/2006 (6:47 pm)
Thanks for such a useful resource. I'll definitely be needing decal wrapping. I'm not familiar with how the decal manager works and am new to Torque engine so I'll take a stab in the dark.

I assume that the gClientContainer.findObjects() should return polys that are within an area defined by smDecalMask?
Maybe this function is not returning all the polys that it should.
#3
05/04/2006 (9:49 pm)
I hadn't thought of it as a resource, really. More of a plea to "Please look at my code and tell me what I'm doing wrong." ;-) Also, I'm not sure how useful this would be for games that don't have a lot of paint flying around. Bullet holes, for example, don't usually need to "wrap" around a set of polys.

I have it far enough now that I'm willing to move on. Several of the issues that remain can be resolved by a better "splat" image.

For the curious:

void DecalManager::addWrapDecal(const Point3F& pos,
                            Point3F normal,
                            Point3F impactNormal,
                            DecalData *decalData)
{
   ...
   DecalExData *exData=new DecalExData;
   newDecal->exData=exData;

   // construct matrix
   Point3F x,y,z;
   y=impactNormal;
   if (mFabs(y.z)>0.001f)
   {
      //mCross(y,Point3F(1,0,0),&z);
      z.set(0,y.z,-y.x);
      z.normalizeSafe();
      mCross(y,z,&x);
      x.normalizeSafe();
   }
   else
   {
      //mCross(y,Point3F(0,0,1),&x);
      x.set(y.y,-y.x,0);
      x.normalizeSafe();
      mCross(x,y,&z);
      z.normalizeSafe();
   }

   // backup a bit (for the transforms) so that we can "see" all the polys
   Point3F position = Point3F(pos.x + (-impactNormal.x * decalData->sizeX*2), pos.y + (-impactNormal.y * decalData->sizeY*2), pos.z + (-impactNormal.z * decalData->sizeX*2));

   MatrixF decalToWorld(true);
   decalToWorld.setColumn(0,x);
   decalToWorld.setColumn(1,y);
   decalToWorld.setColumn(2,z);
   decalToWorld.setColumn(3,position);

   exData->mTransform=decalToWorld;

   MatrixF worldToDecal=decalToWorld;
   worldToDecal.inverse();

   // setup depth sort list
   // expanded to allow the image to stretch
   Point3F extent(decalData->sizeX*5,decalData->sizeY*5,decalData->sizeX*5);
   smDepthSortList.clear();
   smDepthSortList.set(worldToDecal,extent);
   smDepthSortList.setInterestNormal(impactNormal);

   // build world space box and sphere around decal
   x *= decalData->sizeX*2;
   y *= decalData->sizeX*2;
   z *= decalData->sizeY*2;
   gDecalBox.max.set(mFabs(x.x)+mFabs(y.x)+mFabs(z.x),
                     mFabs(x.y)+mFabs(y.y)+mFabs(z.y),
                     mFabs(x.z)+mFabs(y.z)+mFabs(z.z));
   gDecalSphere.radius = gDecalBox.max.len();
   gDecalSphere.center = pos;
   gDecalBox.min  = pos - gDecalBox.max;
   gDecalBox.max += pos;

   // get polys
   gClientContainer.findObjects(smDecalMask,DecalManager::collisionCallback,this);

   // setup partition list
   gDecalPoly[0].set(-decalData->sizeX,0,-decalData->sizeY);
   gDecalPoly[1].set(-decalData->sizeX,0,decalData->sizeY);
   gDecalPoly[2].set(decalData->sizeX,0,decalData->sizeY);
   gDecalPoly[3].set(decalData->sizeX,0,-decalData->sizeY);

   exData->mPartition.clear();
   exData->mPartitionVerts.clear();
   smDepthSortList.depthPartition(gDecalPoly,4,exData->mPartition,exData->mPartitionVerts);

   if (exData->mPartitionVerts.size()>0)
   {
      // now set up tverts
      exData->mPartitionTVerts.setSize(exData->mPartitionVerts.size());
      F32 invSizeX = 1.0f / (decalData->sizeX);
      F32 invSizeY = 1.0f / (decalData->sizeY);
      for (S32 i=0; i<exData->mPartitionVerts.size(); i++)
         exData->mPartitionTVerts[i].set(0.5f + 0.5f * exData->mPartitionVerts[i].x * invSizeX,0.5f + 0.5f * exData->mPartitionVerts[i].z * invSizeY);
   }
   else
   {
      delete exData;
      newDecal->exData=NULL;
      // do it the old fashioned way
      ...
   }
   ...
}

ascendantmoon.com/images/pbn-06-05-04-004.jpg
ascendantmoon.com/images/pbn-06-05-04-005.jpg
-David
#4
05/04/2006 (10:38 pm)
Is that a paintball chaingun? >.>
#5
05/05/2006 (11:04 am)
@Paul: Yah. Though it's still waiting for animation and proper scripting. Right now it's just "the last gun model I loaded up for testing purposes". Plus, I just love seeing it's "cartoon chrome". :)

-David
#6
05/05/2006 (11:08 am)
Hehe. I'm glad to see you're working on the Paintball game again, David. :)

-Eric
#7
05/05/2006 (10:42 pm)
That paint splat decal could easily be scorch mark from an explosion...