1
\$\begingroup\$

I'm currently developing a small game for my own enjoyment and I've ran into a problem. As you probably know many third person games (such as Runescape and XCOM, for example) hide the roofs when you enter a building so that you can still see your character. I'm using lwjgl and my current solution is to use clipping planes to simply stop rendering everything above a certain height when the player is inside of a building, but this gives bad results because if there are other buildings or tall entities nearby they are also not rendered above that plane.

I've thought of some solutions - one of them being to simply hide certain faces/vertices when I'm in certain areas. This could also be applied to other things than just buildings as well. Is this the right way to approach this problem? Or is there a better way to handle this?

PS. I'm not very experienced with openGL (especially shaders), so if the solution requires shader work I might need more detailed descriptions.

\$\endgroup\$
1
\$\begingroup\$

When you get more advanced:

-You'll end up using instancing to draw ALL of the roofs at the same time with one DrawInstanced(...) call. In that setup, we can detect which roof we're under (@GarMan) and just omit that one roof instance.

-You could also activate the stencil buffer, draw the roof we're under (without outputting any colors), deactivate the stencil, the draw all of the roofs (the stenciled roof is omitted). Clear the stencil and continue rendering the scene.

There are as many "best ways" to do it as there are people with opinions. I agree with GarMan that the most common and simplest approach is to use bounding shapes to represent volumes; BoundingBox, BoundingSphere, etc.

foreach (roof in g_RoofList)
{
  If (roof->boundingShape.Contains(camera->LookAt))
    //Do nothing
  else
    roof.Draw();
}

//or maybe

foreach (building in g_BuildingList)
{
  building->DrawFloor();
  building->DrawWalls();
  //Draw the roof, or the objects inside, but not both
  If (building->boundingShape.Contains(camera->LookAt))
    building->DrawRoof();
  else
    building->DrawInteriorObjects();
}

Since you are just beginning, I'd recommend focusing on "doing it" before "doing it right". There's really no getting around the amount of face-planting you'll be doing as you progress, looking back at your previous creations. It will feel like a stranger wrote them.

Edit per comment:

void RenderScene()
{
  EnableRoofClip();
  //Render only the buildings that contain camera->LookAt
  foreach (building in g_BuildingList)
  {
    If (building->boundingShape.Contains(camera->LookAt))
      building->Draw();
  }

  DisableRoofClip();    
  //Render buildings that do not contain camera->LookAt
  foreach (building in g_BuildingList)
  {
    If (!building->boundingShape.Contains(camera->LookAt))
      building->Draw();
  }
}

...one of them being to simply hide certain faces/vertices when I'm in certain areas. This could also be applied to other things than just buildings as well. Is this the right way to approach this problem? Or is there a better way to handle this?

I don't recommend implementing the following in your game because you already have a method that works, however, you can cull vertices on the GPU in a variety of ways and this is one of them:

VertexShaders create an output for every input. GeometryShaders can take that output and then choose, on a per-vertex basis, which ones to throw away. As a starting point, implement a simple GeometryShader that outputs every input (does nothing). Then, you can experiment.

There's no, one, right way and there are usually always better ways.
As soon as you implement a better way, you'll find another, even-better, way:

https://en.wikipedia.org/wiki/Feature_creep

\$\endgroup\$
  • \$\begingroup\$ Very good suggestions, I'll probably implement one of them (never used stencil buffer - I'll probably use that for practice). But is there a way to do it without rendering the roof as a separate entity? \$\endgroup\$ – Charanor Feb 29 '16 at 15:46
  • \$\begingroup\$ @Charanor, use BoundingBox to figure out which one you are currently in. Draw only that one, with the roof cut off, using your current method. Then, disable the clipping and draw all of them except that one. Get your game to 90% completion before you go back to improve your roof clipping procedure; it might not actually need improving. \$\endgroup\$ – Jon Feb 29 '16 at 20:14
  • \$\begingroup\$ Oh, I never thought of doing that. Is this the BoundingBox class you're talking about? (docs.oracle.com/javafx/2/api/javafx/geometry/BoundingBox.html) \$\endgroup\$ – Charanor Mar 1 '16 at 14:42
  • \$\begingroup\$ @Charanor, looks good. The inner workings are really simple. A point is inside a rectangle if it is ((left of the right side) AND (right of left side) AND (above the bottom side) AND (below the top side)). Here is an example rectangle-rectangle collision: developer.mozilla.org/en-US/docs/Games/Techniques/… \$\endgroup\$ – Jon Mar 1 '16 at 17:28
0
\$\begingroup\$

A solution is to use simplistic collision detection between your camera and the characters being rendered. For example you could wrap buildings in simple boxes, collide some rays from your view port (4 corners and center) to the character, and any boxes your ray goes thru needs to be rendered differently.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.