Game Development Community

Hiding interiors

by Rubes · in Torque Game Engine · 04/05/2008 (8:53 pm) · 16 replies

Hoping for some good insight here.

I've been looking into the possibility of hiding interior (DIF) objects on the fly, but with only partial success. I currently hide and unhide DTS objects as needed in my game by using the ShapeBase::setHidden() method, and I would like to do the same with DIF objects -- but it is proving to be a bit more complicated.

First, I just defined a console method for the InteriorInstance class that calls setHidden() on the object. Nothing happened when I called it -- I presume it's because this is calling SimObject::setHidden(), which does something different than the ShapeBase::setHidden() method.

The ShapeBase::setHidden() method really just calls either removeFromScene() or addToScene(), so I set up an InteriorInstance::setRenderStatus() method to do the same thing, using a new boolean mRenderMe to keep track:

void InteriorInstance::setRenderStatus(bool render)
{
   if (render != mRenderMe) {
      if (mRenderMe) {
         removeFromScene();
         Parent::onRemove();
      }
      else
         addToScene();
		
      mRenderMe = render;
   }
}

This results in interiors being removed just fine, but they're not able to be added back again, so there is something other than addToScene() that must be done.

My understanding of this code is pretty thin, so I'm hoping for a little help. I did notice someone trying to do this a while back (in this thread), but it looks like they tried similar things and no solution was posted.

#1
04/06/2008 (12:11 am)
I can be a little slow on picking some things up, but why not portalize the interior? That is what portals are for I thought. For rendering indoor difs only when you look at them.

As I said I can be a little slow with this kind of stuff so feel free to correct me if I am wrong.


NickyB
#2
04/06/2008 (1:06 am)
Portals are a way to reduce render overhead while inside of a DIF. But they do nothing if you are outside of the DIF.

Rubes you might take a look at this resource : http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3751 It deals with adjusting how the engine occludes objects if they are hidden by the terrain. You might be able to look at what code is performing the occlusion (hiding) and hook into it for what your own purpose is. Please let us know what you discover.
#3
04/06/2008 (11:16 am)
@Nicholas: You're not slow, that's actually the reason for this question. We're having so much difficulty getting portals to work with our DIFs that it would require almost complete remodeling of our structures, or finding a solution like this. I've already grouped by DTS shapes by my own "zones" and can hide and unhide them as needed, but I'm unable to do the same with the DIF structures.

@Jason: Thanks for the heads up, I'll definitely check that out.
#4
04/06/2008 (12:00 pm)
DIF Portals with constructor should work without any problem. And DIFs actually portal correctly when outside.

But the really interesting question is: why don't you use DIF LoD?
#5
04/06/2008 (12:10 pm)
Understood, Marc, but the fact of the matter is that they don't. These are models that were originally created in Quark, then moved over to Constructor later on, and portals weren't included from the start. So going back to these models and trying to add portals has ben fruitless. The models are fairly large and complex, and given the persnickety nature of portals it is proving to be extremely difficult to track down the many reasons why the portals are not working. So instead of causing my modeler too much grief, I'm looking into other solutions.

And yes, we are using LOD with our DIFs. But in many cases, this is an issue of visibility, not distance. In many places there are situations where certain DIFs can be completely hidden from the player due to visibility, although the player physically is only a short distance away from the DIF. LOD wouldn't work well in that situation.
#6
04/06/2008 (1:44 pm)
@Rubes:

It might be easier just to make a modification to the engine, and set a "visibility" flag on the dif. In the C++ code, if the DIF invisibility flag is set, just return immediately out of the render and collision checking functions.

Also, you mention portal problems - there have been many fixes in the past few weeks for portals. Have you tried the latest constructor in the forums?
#7
04/06/2008 (2:54 pm)
@Jaimi: Thanks...yes, I've been working with N.R. on the portal issue, and we haven't been able to solve it at all. I've been following all of your updates, and we haven't had any luck with them. There may just be too many issues introduced either by Quark or the move from Quark to Constructor to get them to work properly without a tremendous number of hours put in.

I've tried making such a modification to the engine, but I can't seem to get it working.

I added a boolean variable mRenderMe to the InteriorInstance class, and added it to the pack/unpackUpdate routines. Then I created console methods to set (and show) its value from script. Finally, I added this short bit of code (in bold) to the InteriorInstance::renderObject() routine to exit out immediately if mRenderMe is false:

void InteriorInstance::renderObject(SceneState* state, SceneRenderImage* sceneImage)
{
   AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");

   if(gEditingMission && isHidden())
      return;
      [b]
   if (!mRenderMe) {
      return;
   }
   Con::printf("mRenderMe is %s.", isRendered() ? "true" : "false");
   [/b]...

Unfortunately, this never seems to fire. I can call the console method to change mRenderMe to false, but it never seems to change it to false as far as the renderObject method is concerned. No matter what I do, the console print statement in the code above always shows mRenderMe to be true -- even if I call the isRendered() method from script, and it says it is false.

I think there is some aspect of the server/client architecture that is screwing me up, but I can't figure it out.
#8
04/06/2008 (7:10 pm)
At last, I realized what the problem was...the console method I wrote to set the visibility variable was not properly dealing with server objects, so I just copied the code from the InteriorInstance 'setDetailLevel' console method, which does this. Now it's working well...I can hide and unhide DIFs on the fly pretty easily, and with a nice frame rate boost.
#9
04/06/2008 (7:43 pm)
I'm going to make the obligatory "make it a resource" post now.
#10
04/06/2008 (8:25 pm)
LOL...I was wondering if/when someone would. I'll get to work on it.
#11
04/06/2008 (9:12 pm)
Quote:And DIFs actually portal correctly when outside

Let me clarify what I meant by this.

If you are standing in front of a building with portals, and it has 60 buildings behind it that you cannot see, they are all rendered even if they are portalized. So the portals might hide what is inside the DIF, but they don't hide the DIF itself when it isn't visible.

A simple LOS check on the DIFs every so often combined with the ability to hide them could potentially increase the render speed of cities and other densely populated regions substantially.

The problem with the resource I'd linked is that while it significantly improves terrain occlusion, it breaks waterblocks. I'm glad you figure out how to hide them though. I'll be making use of it too :P
#12
04/07/2008 (8:30 am)
There are actually a few things about this code that I'm not certain of, so I thought I would try posting it here first before submitting it as a resource, and perhaps others here can answer a couple of questions I have about it.

Basically, the idea is like Jaimi mentioned: setting a DIF invisibility flag and just returning immediately out of the render function if set. So it's fairly simple, and involves only engine/interior/interiorInstance.h and .cc.

The first step is to define the flag and appropriate routines in interiorInstance.h:

[b]
bool mVisible;
void setVisibility(bool visible);
bool isVisible() { return mVisible; }
[/b]

The next step is to implement the following code in interiorInstance.cc. The first thing is to add the following to the constructor:

InteriorInstance::InteriorInstance()
{
   mAlarmState             = false;
   mDoSimpleDynamicRender  = false;
   mUseGLLighting          = false;[b]
   mVisible                = true;[/b]
   ...

Next, we add the function to change the visibility on demand:

[b]
void InteriorInstance::setVisibility(bool visible)
{
    if (mVisible != visible) {
        mVisible = visible;
   }
}
[/b]

...and the console method to do this from script (as well as to perform a visibility check):

[b]
ConsoleMethod( InteriorInstance, setVisible, void, 3, 3, "(bool visible?)")
{
   if(object->isServerObject())
   {
      NetConnection * toServer = NetConnection::getConnectionToServer();
      NetConnection * toClient = NetConnection::getLocalClientConnection();
      if(!toClient || !toServer)
         return;
		
      S32 index = toClient->getGhostIndex(object);
      if(index == -1)
         return;
		
      InteriorInstance * clientInstance = dynamic_cast<InteriorInstance*>(toServer->resolveGhost(index));
      if(clientInstance)
         clientInstance->setVisibility(dAtob(argv[2]));
   }
   else
      object->setVisibility(dAtob(argv[2]));
}

ConsoleMethod( InteriorInstance, isVisible, bool, 2, 2, "")
{
   return object->isVisible();
}
[/b]

Finally, the code to skip over the rendering of the DIF:

void InteriorInstance::renderObject(SceneState* state, SceneRenderImage* sceneImage)
{
   AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");

   if(gEditingMission && isHidden())
      return;
[b]
   if (!mVisible) {
      return;
   }
[/b]
   PROFILE_START(InteriorRenderObject);
   ...

So with this code, each DIF will have methods available in script to check its current visibility (%interior.isVisible()) and set its visibility to TRUE or FALSE (%interior.setVisible()). Seems to work in my TGE 1.5.2-based game environment, although I have not yet tested this on a fresh 1.5.2 install or on any previous versions. Also, my game is exclusively single-player, so I have not tested this in a multiplayer environment.

So with the above, I have a couple of questions:

1. In my first go at this, the console method 'setVisible' only called object->setVisibility(), but this didn't accomplish anything -- mVisible was never updated to its new value. So I looked at the console method 'setDetailLevel', which seemed to accomplish a similar task, and I just copied that code -- which works. But my impression is that this might make DIFs visible or invisible on all clients, not just the client requesting it, which could be a problem in multiplayer games. Is that right?

2. Along the same lines, in my game script, I set up the call to set a DIF's visibility by using a commandToServer call, but I imagine that DIF visibility is a client-specific thing. Is it better to call %interior.setVisible() from the client, then?

3. Originally, I included mVisible in the packUpdate() and unpackUpdate() routines, but later I removed them and it still seems to work fine. I'm not great with the networking code, but is it necessary to include this?

4. I did nothing to specifically deal with collision checking if the DIF is invisible, so collision checking is intact. I was looking to primarily increase rendering speed, so I didn't look into this. Does anyone think it would be beneficial to add this as well?

Thanks...
#13
04/07/2008 (4:33 pm)
I'm still pretty inexperienced with some of the engine networking code... but when using console methods to modify object properties (like the sun's azimuth or an fxrenderobject's position) I typically have modified the code to expose an InspectPostApply for the object. That allows me to quickly update the object without touching the packet functions.

I'm not sure if it would apply here... I'll have to give it a shot later. I was thinking you'd just set the DIF to invisible then call its inspectpostapply and see if it updated correctly.
#14
04/07/2008 (5:42 pm)
My understanding of InspectPostApply is that it basically is only called server-side, and it functions by signalling which data needs to get transferred to the client by ultimately going through packUpdate.

The thing that is confusing for me here is that I would think the client is the only one interested in whether a DIF is visible or not, since the rendering or hiding of a particular DIF should be specific to each client. If the server is told which DIFs should be hidden, then I would imagine all clients would then display DIFs the same way.
#15
04/07/2008 (7:55 pm)
Oh you know, you are right. Argh, so confusing. So yes this is something each client needs to decide for itself. Though there could be situations where hiding a DIF for everyone makes sense I guess...
#16
04/08/2008 (9:07 am)
I think, then, that I'll wait on posting this as a resource until someone more knowledgeable than me can comment on the above questions. The code currently works fine for me in single-player mode, but I suspect it won't work well in multiplayer mode. At least the code will be here for anyone looking to try it out.