Sunlight Direction w/TGE 1.5.2 and Cel Shading resource
by Meredith F. Purk II · in Torque Game Engine · 10/17/2010 (5:14 pm) · 12 replies
I'm using DavidRM's Cel Shading in TGE resource which I merged into TGE 1.5.2. The biggest challenge was getting the sunlight direction code to work since the original Cel Shading code used the old light manager pre-Torque Lighting Kit. Since the discussions at the end of the resource thread died off regarding implementation in TLK/TGE 1.5.2 over a year ago, I managed to work something out on my own:
It let's it compile/shade, but celshading is rendering with the sunlight position opposite that which the shadow rendering is using, so the light appears to hit the model on the shadow side.
Is anyone familiar with getting sunlight direction from TLK? Even better, is anyone familiar with the Cel Shading resource itself?
(This is what the entire function the above code is in looks like)
LightInfo *sunLight = gClientSceneGraph->getLightManager()->sgGetSpecialLight(LightManager::sgSunLightType); VectorF lightVector = sunLight->mDirection; MatrixF worldToObj=*objectTransform; worldToObj.inverse(); worldToObj.mulV(lightVector); lightVector.normalize();
It let's it compile/shade, but celshading is rendering with the sunlight position opposite that which the shadow rendering is using, so the light appears to hit the model on the shadow side.
Is anyone familiar with getting sunlight direction from TLK? Even better, is anyone familiar with the Cel Shading resource itself?
(This is what the entire function the above code is in looks like)
void TSMesh::renderCelShade(S32 frame, S32 matFrame, TSMaterialList * materials, const MatrixF *objectTransform)
{
// most gl states assumed to be all set up...
// if we're here, then we're drawing cel shading in two passes...
if ((objectTransform==NULL) || (!dglDoesSupportARBMultitexture()))
return;
S32 firstVert = vertsPerFrame * frame;
const Point3F *normals = getNormals(firstVert);
LightManager * lightManager = gClientSceneGraph->getLightManager();
/*Point3F lightPos=this->getCenter();
objectTransform->mulP(lightPos);
if (lightManager->getSunLight())
lightPos-=lightManager->getSunLight()->mPos;
else
lightPos=-lightManager->getShadowLightDirection();*/
//VectorF lightVector=lightPos;
LightInfo *sunLight = gClientSceneGraph->getLightManager()->sgGetSpecialLight(LightManager::sgSunLightType);
VectorF lightVector = sunLight->mDirection;
MatrixF worldToObj=*objectTransform;
worldToObj.inverse();
worldToObj.mulV(lightVector);
lightVector.normalize();
// set up vertex arrays -- already enabled in TSShapeInstance::render
glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]);
glNormalPointer(GL_FLOAT,0,normals);
// create vertex texture coordinates
static Vector<F32> cstverts;
cstverts.setSize(vertsPerFrame);
for (S32 vv=0; vv<vertsPerFrame; vv++)
{
Point3F tmpNormal=normals[vv];
// dot product of the vertex normal and light
F32 dotP = mDot(tmpNormal,lightVector);
if (dotP<0.0)
dotP=0.0;
cstverts[vv]=dotP;
}
glTexCoordPointer(1,GL_FLOAT,0,cstverts.address());
// lock...
bool lockArrays = dglDoesSupportCompiledVertexArray();
if (lockArrays)
glLockArraysEXT(0,vertsPerFrame);
for (S32 i=0; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::render: rendering of non-indexed meshes no longer supported");
if (!(draw.matIndex & TSDrawPrimitive::NoMaterial))
{
S32 flags = materials->getFlags(draw.matIndex & TSDrawPrimitive::MaterialMask);
// don't draw on translucent material - issues with env mapping
if (flags & TSMaterialList::Translucent)
continue;
}
glDrawElements(getDrawType(draw.matIndex>>30),draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
}
// unlock...
if (lockArrays)
glUnlockArraysEXT();
}
#2
10/18/2010 (2:55 am)
Well, there's the obvious easy fix of negating the direction vector before using it ;). But what's this line doing?worldToObj.inverse();I'm not too hot on matrix maths in Torque, but it seems like this line isn't necessary. Essentially you're getting the world-to-object transform, turning it into the object-to-world matrix, then applying this transform to the sun direction. Surely you should just use the world-to-object transform to turn the sun vector into a local vector?
#3
So... I just left it in. Well, for now I'll just negate the vector, that does seem to work fine for now. I just wanted to make sure I was getting the sunlight direction correctly (and not just getting lucky and happening to get something that is working by fluke).
10/18/2010 (11:29 pm)
I'm glad I'm not the only one unsure of the worldtoObj.inverse() line. I've tried with and without it with no noticable differences, but the original code had that line present, as did several changes to the code revised afterward.So... I just left it in. Well, for now I'll just negate the vector, that does seem to work fine for now. I just wanted to make sure I was getting the sunlight direction correctly (and not just getting lucky and happening to get something that is working by fluke).
#4
10/18/2010 (11:56 pm)
Hmm, that's odd. Maybe check the definition of .inverse(), since it might return a new matrix instead of modifying the object itself. If that's the case, it might be that whoever wrote that forgot to write something like worldToObj = worldToObj.inverse().
#5
01/15/2011 (8:15 am)
@Meredith: Did you ever resolve this issue? How did you 'negate the vector'?
#7
It works for now, so the light hits the model on the correct side, rather than on the shadow side. Here is what lines 21-26 of the code posted in the body of this thread now look like:
01/15/2011 (1:31 pm)
What he said - lightVector.neg();It works for now, so the light hits the model on the correct side, rather than on the shadow side. Here is what lines 21-26 of the code posted in the body of this thread now look like:
LightInfo *sunLight = gClientSceneGraph->getLightManager()->sgGetSpecialLight(LightManager::sgSunLightType); VectorF lightVector = sunLight->mDirection; lightVector.neg(); // Temporary line - trying something MatrixF worldToObj=*objectTransform; worldToObj = worldToObj.inverse(); worldToObj.mulV(lightVector); lightVector.normalize();
#8
One observation, the cel shading mod seems to keep other shapes [trees, etc.] from being lit properly - any ideas?
01/18/2011 (2:27 pm)
Thanks guys, this is great!!!One observation, the cel shading mod seems to keep other shapes [trees, etc.] from being lit properly - any ideas?
#9
Depending on how you are working your TLK lighting code and which objects you are cel shading it can affect the lighting that objects receive. By default, DavidRMs resource disables the TLK lighting in favor of the cel shading, in which case if you are not cel shading your tsstatics like trees then they are being washed out/over-exposed.
I'll take some time after I get off work tomorrow to put together some example and some code... in the meantime, you can look at TSStatic::renderObject and compare the code changes there with the ones made in player.cc to turn on cel shading if you haven't already. I think it was around line 304 for me...
01/19/2011 (2:03 am)
First, I assume you've already been referencing the original celshading resource (http://www.torquepowered.com/community/resources/view/10318). Near the very bottom, DavidRM talks about how you can turn cel shading on/off for different objects and gives an example by showing you how to activate it for players in player.cc.Depending on how you are working your TLK lighting code and which objects you are cel shading it can affect the lighting that objects receive. By default, DavidRMs resource disables the TLK lighting in favor of the cel shading, in which case if you are not cel shading your tsstatics like trees then they are being washed out/over-exposed.
I'll take some time after I get off work tomorrow to put together some example and some code... in the meantime, you can look at TSStatic::renderObject and compare the code changes there with the ones made in player.cc to turn on cel shading if you haven't already. I think it was around line 304 for me...
mShapeInstance->setupFog(fogAmount,state->getFogColor()); mShapeInstance->animate(); // celshade resource // mShapeInstance->render(); MatrixF mat=getRenderTransform(); mShapeInstance->render(&mat); // celshade resource
#10
Thank you! The tip for TLK lighting was the trick. it led me to this post:
http://www.torquepowered.com/community/forums/viewthread/43511
Which had the code solution.
I'm interested to see any code you can share. My main issue now is that I have some 'dummy weapons' mounted to vehicles that are being outlined. Just have to figure out how to only outline visible objects. Any ideas???
01/19/2011 (12:02 pm)
@Meredith:Thank you! The tip for TLK lighting was the trick. it led me to this post:
http://www.torquepowered.com/community/forums/viewthread/43511
Which had the code solution.
I'm interested to see any code you can share. My main issue now is that I have some 'dummy weapons' mounted to vehicles that are being outlined. Just have to figure out how to only outline visible objects. Any ideas???
#11
Because by default, you turn on celshade and outline for all shapes of that class once you push the transform into the rendershape function. Is it only mounted shapes that are outlining while invisible, or have you had other shapes do the same? I admit I've never experimented with invisible objects. I wanted to get the code to compile/work but I'm no where near that point on my project where I'm worried about visual polish yet. (I'll burn that bridge when I get there. ;) )
01/20/2011 (1:05 am)
Mmm... you have invisible dummy weapons that are being outlined, is that what you are saying? I could be wrong (there may be a simpler way) but the only thing I can think of right now would be modifications to the code to check a flag on the shape itself to determine whether or not to pass the transform (i.e., render the celshade/outlines).Because by default, you turn on celshade and outline for all shapes of that class once you push the transform into the rendershape function. Is it only mounted shapes that are outlining while invisible, or have you had other shapes do the same? I admit I've never experimented with invisible objects. I wanted to get the code to compile/work but I'm no where near that point on my project where I'm worried about visual polish yet. (I'll burn that bridge when I get there. ;) )
#12
01/20/2011 (1:27 am)
...What I've done for now is offset the dummy weapons closer to the vehicle body so that the outlines overlap... bit of a hack but works for now...
Torque 3D Owner Meredith F. Purk II
Default Studio Name