Game Development Community

Scaling up a DTS object causes wrong MIP level to render

by Geom · in Torque Game Engine · 09/04/2008 (9:50 pm) · 6 replies

I've found that scaling the size of a DTS object causes Torque to render the DTS's textures at the wrong MIP level. The bigger the scale of the DTS, the lower-resolution the MIP level becomes.

Here's a picture illustrating the problem:

redbrickgames.com/pix/080904/mip_issue.png

Both windmills have scaling of 2.0. The one on the right is slightly farther from the camera, and you can see how its MIP level is clearly too low-res.


I found two old threads that note this issue. But neither thread has much of a fix, and the two threads seem to contradict each other:


Thread #1 - LOD, Mip-Mapping Questions

Pat Wilson wrote:
Quote:You probably want to look for the GL call to adjust the MipMap LOD Bias. I don't know what this is, off the top of my head, but that is what you want to do.

...which suggests, to fix the problem, some new OpenGL calls need to be added to the engine.


Thread #2 - Bad MipMapping on .DIF objects

BobTheCBuilder wrote:
Quote:This actually has to do with how Torque handles mip-mapping on a software level (at least for dts's). When Torque decides to drop into a lower texture detail level (not sure why this is even necessary nowadays) it doesn't seem to take the scale of the object or surface texture into account. Detail levels based on texture scale could be difficult, but as far as the object scale this appears to be a bug.

...which suggests, Torque's detail-selection code is buggy, because it ignores SceneObject::mObjectScale somewhere.


me <-- perplexed.

Can anybody help with this problem?

Unfortunately, I'm no OpenGL expert :-( so I'm hoping the 2nd thread is correct, and the solution doesn't require l33t OpenGL skillz.

TIA - I've been pondering this problem for a while, and I'm pretty much stuck.

#1
09/05/2008 (4:51 am)
Here's what I did for InteriorInstances; basically threw object scale into the calculation that decides to use lowres textures:

if (dist != 0.0)
	   {
		  F32 length = dglProjectRadius(dist, 1.0f / pInterior->mAveTexGenLength);
		  if (length * getScale().x < (1.0 / 16.0)) // Sam: scale of interior instance was added here to stop lowres being selected too close with enlarged object
		  {
			 TextureManager::setSmallTexturesActive(true);
		  }
	   }


Something similar would probably work with DTSs?
#2
09/05/2008 (11:05 am)
Groovy, thanks for the lead! I'll check to see if the DTS code has something similar to that...
#3
09/05/2008 (11:50 am)
Hey Geom,
if you find something like that, could you post it here ?
#4
09/05/2008 (12:39 pm)
W00t! I think I found the problem.

The short of it is, the problem is almost gone for anyone using Torque 1.4 and later. Almost. The reason I'm still seeing it is, I'm still working with the TGE 1.3 codebase. TGE 1.4+ will still exhibit the problem, but only for very highly-scaled DTS shapes. The problem's still there, but the scale would have to be really big.

@Orion
sure thing...I'll post shortly once I verify it's working.
#5
09/05/2008 (1:00 pm)
Hmm... and the change to Torque 1.4 has a side effect of all-but-disabling "small textures" on DTS's. "Small textures" seems to be a feature meant to save memory on the video card.

So for anyone who wants small textures, it could be argued that Torque 1.4 introduced a new bug.
#6
09/05/2008 (2:45 pm)
The fix

Open /engine/game/shapeBase.cc, and find the function ShapeBase::renderObject().

In it, replace this old block of code:
// This is something of a hack, but since the 3space objects don't have a
   //  clear conception of texels/meter like the interiors do, we're sorta
   //  stuck.  I can't even claim this is anything more scientific than eyeball
   //  work.  DMM
   F32 axis = (getObjBox().len_x() + getObjBox().len_y() + getObjBox().len_z()) / 3.0;
   F32 dist = (getRenderWorldBox().getClosestPoint(state->getCameraPosition()) - state->getCameraPosition()).len();
   if (dist != 0)
   {
      F32 projected = dglProjectRadius(dist, axis) / 25;
      if (projected < (1.0 / 16.0))
      {
         TextureManager::setSmallTexturesActive(true);
      }
   }

...with this new block of code.
// This is something of a hack, but since the 3space objects don't have a
   //  clear conception of texels/meter like the interiors do, we're sorta
   //  stuck.  I can't even claim this is anything more scientific than eyeball
   //  work.  DMM
   const Box3F& box = getObjBox();
   Point3F scaledSize = (box.max - box.min) * getScale();
   F32 axis = (scaledSize.x + scaledSize.y + scaledSize.z) / 3.0f;
   F32 dist = (getRenderWorldBox().getClosestPoint(state->getCameraPosition()) - state->getCameraPosition()).len();
   if (dist != 0)
   {
      F32 projected = dglProjectRadius(dist, axis) / 350;
      if (projected < (1.0 / 16.0))
      {
         TextureManager::setSmallTexturesActive(true);
      }
   }

That should fix blurry DTS textures for any scale factor, and still use the "small textures" memory-saving feature. It should work on any TGE version from 1.3 to 1.5.2, and also on the RTS-SK.

For anyone who's interested, here's what I learned:


The explanation

It turns out, there are *two* mechanisms in Torque that select the detail level of a texture. One mechanism is OpenGL's native MIP level selection. That's pretty much invisible to the programmer; OpenGL handles it automatically. The other mechansim is Torque "small textures" feature. As Sam pointed out, Torque has code that manually switches between "normal" and "small" textures. That's where the problem was; this code wasn't considering object scale when deciding when to switch.

Small textures are 1 / 16th the size on edge of normal textures, and thus take up 1 / 256th the memory footprint. A small texture has a separate, distinct OpenGL texture from its equivalent, "normal" texture. In other words, there are *two* OpenGL textures, created for each *one* TextureHandle in Torque (for .DTS's and .DIFs).

Like I said earlier, small textures seems to be a memory-saving feature for the video card. If you're looking at a bunch of DTS objects, but they're all far away, the video card only has to load the small textures into memory, not the normal textures.

The problem with Torque 1.4 and later is that it basically turned off small textures. It simply changed the threshold of switching to small textures by about a factor of 14 (see in the code above, where the constant "350" got changed to "25") So, 1.4 mostly got rid of the problem (until you hit scales of maybe 14x-20x), but I'm assuming small textures are still a useful feature (?) and we don't want to just get rid of them like that.

So my fix not only factors in the object scale, but it also changes that constant from "25" back to "350", like it was in Torque 1.3. That re-enables small textures. Seems to work great! I tested it with objects scaled up to 30x.

I got most of my info about small textures from the comments in gTexManager.h.