0
\$\begingroup\$

I'm in the process of teaching myself game development, and I'm trying to learn 3D, and started on a basic 3D game. The actual problem I've encountered is with level design, I'm using cubes all over the world to define the levels.

This is how my level file is like.

# P stands for Player
# F stands for Floor
# N stands for Cone pointing North
# E stands for Cone pointing East
# W stands for Cone pointing West
# S stands for Cone pointing South
# G stands for goal sphere

# The initial direction of the camera
!NORTH

# The message rendered on each level
@Use WASD keys to move Blox
@Use SPACE key to jump

   FFF
  FFFFF
    G
 FFFFFFF
   FFF
   FFF
   FFF
   FFF
    F
   FFF
   FFF
   FFF
   FFF
 FFFPFFF
  FFFFF

This works, and I can render my game, but the problem is it lags on low power machines. When using individual cubes, I have 17 vertices per each one. I'm rendering the meshes through GL_TRIANGLE_STRIP. This is how the level is in the game.

And what I want to do, is instead of designing the level one cube at a time, I want to combine multiple small cubes into some large cubes, making the level look like this instead.

And next, I designed the sphere (it's the goal in the game) in Blender and it reports that there are 1984 vertices in that model, which I'm loading into my game via OBJ format. I'm using mesh to mesh collisions in the game (that too as a learning experience) and this game as it is now is lagging by 10-15 FPS on my 3 year old laptop which can play GTA IV at a good frame rates.

So I clearly know I'm going in a wrong direction, but I'm not sure how to implement this in a correct way. This is the information that I got on my desktop and my laptop respectively.

On my desktop:

My desktop is a gaming PC, with an i7-4790k running at 4 GHz and a GTX 750 Ti GPU and 16 GB DDR3 RAM. It runs my game at 60 logic cycles per minute and 30 render cycles per minute. The task manager is saying that my application is using 17% of the CPU and 96 MB of memory.

On my laptop:

My laptop is a Lenovo G400s, with an i5, GT 720M GPU and 4 GB of ram. It runs my game at 60 logic cycles per minute and 25 render cycles per minute. The task manager is saying that my application is using 82% of the CPU and 92 MB of memory.

After looking at the data above, I'm very sure that the issue is in the level design, but I also wonder how large cube world games such as MineCraft keep to manage such high FPS?

What are the other areas I should look into to improve this performance? Any tips are very much appreciated.

Thanks in advance.

\$\endgroup\$

closed as too broad by MichaelHouse Apr 3 '15 at 14:14

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ Are you willing and able to use a geometry shader? Would make this rather trivial. \$\endgroup\$ – Jon Apr 3 '15 at 14:03
  • \$\begingroup\$ Profile the code, see where the slowdowns are. \$\endgroup\$ – MichaelHouse Apr 3 '15 at 14:15
  • \$\begingroup\$ @Byte56 I did the profiling before posting this question here, it said that most of it's time, it's spending at glfwSwapBuffers function. If I disable vsync via glfwSwapInterval(0) then I'm getting 400-415 FPS on both my systems. \$\endgroup\$ – Sri Harsha Chilakapati Apr 3 '15 at 14:18
  • \$\begingroup\$ Please update the question with that information, along with what you've tried to do to fix that, and improve your title to be more specific to the problem you're facing. Thanks. \$\endgroup\$ – MichaelHouse Apr 3 '15 at 14:41
  • \$\begingroup\$ @SriHarshaChilakapati I too have noticed bad framerate issues with the default glfwSwapInterval, even with only rendering 2 triangles. Setting it to 0 has fix rendering smoothness by quite a bit. If I wanted to do vsync I would just to it manually by sleeping a set amount of time in the game loop. \$\endgroup\$ – Ryan Capote Apr 3 '15 at 15:44
2
\$\begingroup\$

You need to look into instancing a unit-cube.

InputElements[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, ..., PER_VERTEX_DATA, 0 },
    { "WORLDMATRIX", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, ..., PER_INSTANCE_DATA, 0 },
    { "WORLDMATRIX", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, ..., PER_INSTANCE_DATA, 0 },
    { "WORLDMATRIX", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, ..., PER_INSTANCE_DATA, 0 },
    { "WORLDMATRIX", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, ..., PER_INSTANCE_DATA, 0 },
};

DrawIndexedInstanced(...);

or:

Cram a description of the cubes into single vertices that a geometry shader can make a cube out of. If each cube is a single vertex, then each vertex is also an instance.

InputElements[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, ..., PER_VERTEX_DATA, 0 },
    { "CUBESIZE", 0, DXGI_FORMAT_R32G32B32_FLOAT, ..., PER_VERTEX_DATA, 0 },
};

Draw(visibleBlockCount, firstVisibleBlockIndex);

Edit:

The sphere and all blocks can be represented with the BoundingXXX classes and quickly tested against each other.

The sphere could be rendered with the same unit-cube by activating the tessellation stage. A sphere is a tessellated cube with all of its points normalized. The distance-from-camera sets the tessellation factor.

Send unit-cube
Hull: Tessellate cube faces into N triangles (varies with camera distance)
Domain: For each triangle, normalize the input points (make spherical, unit-scaled)
Domain: output "new" triangle instead
Domain: World(per-instance) + ViewProjection

This renders an N-sided polyhedron with dynamic LOD.

Edit2:

You didn't ask about the cone but, it, too, would be easy using the tessellation stage.

Send unit-pyramid as 5 control points (4 for circle's bounding box, 1 for tip)
Hull: Store 5th point as hull constant
Hull: Tessellate square into N line segments (varies with camera distance)
Domain: For each line, normalize the input points (make circular, unit-scaled)
Domain: output two triangles instead (line + tip) and (line + (0,0))
Domain: World(per-instance) + ViewProjection

This renders an N-sided pyramid with dynamic LOD.

Edit3:

One last idea..

Again using the geometry shader, feed an indexed line-list (pairs of points) and output cubes instead. Vertex would need to include CubeWidth and width of the current cube is always from input[0]. Since these are indexed draws, accessing the vertex buffer through a ring-buffer-style wrapper will produce infinite maps with only a handful of vertices to update.

enter image description here

Your first image indicates a maximum of 16 cubes on the screen (17 vertices, 32 indices)
Once the index buffer is full, when a cube rolls off the screen:
Delete the second index
Duplicate the last index
Move the first index to the end
Load and/or store the new cube's data into the vertex having the index that you moved
Upload the buffers

This renders an infinite map using 17 vertices, 32 indices, and one draw call

\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer, I'll now look into instanced rendering. By the way, is the code you have given there is DirectX? I'm using OpenGL though, but I think the concept will be same. \$\endgroup\$ – Sri Harsha Chilakapati Apr 3 '15 at 14:14
  • \$\begingroup\$ No problem. You'll end up with only 8 vertices, a handful of indices, and a WorldMatrix for each block stuffed into a second vertex buffer. \$\endgroup\$ – Jon Apr 3 '15 at 14:17
  • \$\begingroup\$ I missed the part about the sphere and revised my answer. \$\endgroup\$ – Jon Apr 3 '15 at 14:25
  • \$\begingroup\$ And the cone.. :) \$\endgroup\$ – Jon Apr 3 '15 at 15:01

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