C++ Game Dev Resources

Posted : admin On 29.12.2020

C is great for game development. I'm working on a 3D platformer, and I've never had any problems with the language. The only problems are collision detection and shading, I just know nothing about them, they would still occur in any other language. The Humble community has contributed over $173,000,000 to charity since 2010, making an amazing difference to causes all over the world. And there you have it. If you know of any other great free game graphics for game devs, let us know in the comments section. And don’t forget to like, share or retweet if you think one of your friends would be interested in this list too! Do you want to create a game 60% faster than with other game engines like Unity, Corona or Cocos2D?

-->

Most games, at some point, load resources and assets (such as shaders, textures, predefined meshes or other graphics data) from local storage or some other data stream. Here, we walk you through a high-level view of what you must consider when loading these files to use in your DirectX C/C++ Universal Windows Platform (UWP) game.

For example, the meshes for polygonal objects in your game might have been created with another tool, and exported to a specific format. The same is true for textures, and more so: while a flat, uncompressed bitmap can be commonly written by most tools and understood by most graphics APIs, it can be extremely inefficient for use in your game. Here, we guide you through the basic steps for loading three different types of graphic resources for use with Direct3D: meshes (models), textures (bitmaps), and compiled shader objects.

What you need to know

Technologies

  • Parallel Patterns Library (ppltasks.h)

Prerequisites

C++ Game Development

  • Understand the basic Windows Runtime
  • Understand asynchronous tasks
  • Understand the basic concepts of 3-D graphics programming.

This sample also includes three code files for resource loading and management. You'll encounter the code objects defined in these files throughout this topic.

  • BasicLoader.h/.cpp
  • BasicReaderWriter.h/.cpp
  • DDSTextureLoader.h/.cpp

The complete code for these samples can be found in the following links.

TopicDescription

Complete code for a class and methods that convert and load graphics mesh objects into memory.

Complete code for a class and methods for reading and writing binary data files in general. Used by the BasicLoader class.

Complete code for a class and method that loads a DDS texture from memory.

C Game Dev Resources Inc

Instructions

Asynchronous loading

Asynchronous loading is handled using the task template from the Parallel Patterns Library (PPL). A task contains a method call followed by a lambda that processes the results of the async call after it completes, and usually follows the format of:

task<generic return type>(async code to execute).then((parameters for lambda){ lambda code contents });.

Tasks can be chained together using the .then() syntax, so that when one operation completes, another async operation that depends on the results of the prior operation can be run. In this way, you can load, convert, and manage complex assets on separate threads in a way that appears almost invisible to the player.

For more details, read Asynchronous programming in C++.

Now, let's look at the basic structure for declaring and creating an async file loading method, ReadDataAsync.

In this code, when your code calls the ReadDataAsync method defined above, a task is created to read a buffer from the file system. Once it completes, a chained task takes the buffer and streams the bytes from that buffer into an array using the static DataReader type.

Here's the call you make to ReadDataAsync. When it completes, your code receives an array of bytes read from the provided file. Since ReadDataAsync itself is defined as a task, you can use a lambda to perform a specific operation when the byte array is returned, such as passing that byte data to a DirectX function that can use it.

If your game is sufficiently simple, load your resources with a method like this when the user starts the game. You can do this before you start the main game loop from some point in the call sequence of your IFrameworkView::Run implementation. Again, you call your resource loading methods asynchronously so the game can start quicker and so the player doesn't have to wait until the loading completes before engaging in early interactions.

However, you don't want to start the game proper until all of the async loading has completed! Create some method for signaling when loading is complete, such as a specific field, and use the lambdas on your loading method(s) to set that signal when finished. Check the variable before starting any components that use those loaded resources.

Here's an example using the async methods defined in BasicLoader.cpp to load shaders, a mesh, and a texture when the game starts up. Notice that it sets a specific field on the game object, m_loadingComplete, when all of the loading methods finish.

Note that the tasks have been aggregated using the && operator such that the lambda that sets the loading complete flag is triggered only when all of the tasks complete. Note that if you have multiple flags, you have the possibility of race conditions. For example, if the lambda sets two flags sequentially to the same value, another thread may only see the first flag set if it examines them before the second flag is set.

You've seen how to load resource files asynchronously. Synchronous file loads are much simpler, and you can find examples of them in Complete code for BasicReaderWriter and Complete code for BasicLoader.

Of course, different resource and asset types often require additional processing or conversion before they are ready to be used in your graphics pipeline. Let's take a look at three specific types of resources: meshes, textures, and shaders.

Loading meshes

Meshes are vertex data, either generated procedurally by code within your game or exported to a file from another app (like 3DStudio MAX or Alias WaveFront) or tool. These meshes represent the models in your game, from simple primitives like cubes and spheres to cars and houses and characters. They often contain color and animation data, as well, depending on their format. We'll focus on meshes that contain only vertex data.

To load a mesh correctly, you must know the format of the data in the file for the mesh. Our simple BasicReaderWriter type above simply reads the data in as a byte stream; it doesn't know that the byte data represents a mesh, much less a specific mesh format as exported by another application! You must perform the conversion as you bring the mesh data into memory.

(You should always try to package asset data in a format that's as close to the internal representation as possible. Doing so will reduce resource utilization and save time.)

Let's get the byte data from the mesh's file. The format in the example assumes that the file is a sample-specific format suffixed with .vbo. (Again, this format is not the same as OpenGL's VBO format.) Each vertex itself maps to the BasicVertex type, which is a struct defined in the code for the obj2vbo converter tool. The layout of the vertex data in the .vbo file looks like this:

  • The first 32 bits (4 bytes) of the data stream contain the number of vertices (numVertices) in the mesh, represented as a uint32 value.
  • The next 32 bits (4 bytes) of the data stream contain the number of indices in the mesh (numIndices), represented as a uint32 value.
  • After that, the subsequent (numVertices * sizeof(BasicVertex)) bits contain the vertex data.
  • The last (numIndices * 16) bits of data contain the index data, represented as a sequence of uint16 values.

The point is this: know the bit-level layout of the mesh data you have loaded. Also, be sure you are consistent with endian-ness. All Windows 8 platforms are little-endian.

Game Development Software

In the example, you call a method, CreateMesh, from the LoadMeshAsync method to perform this bit-level interpretation.

CreateMesh interprets the byte data loaded from the file, and creates a vertex buffer and an index buffer for the mesh by passing the vertex and index lists, respectively, to ID3D11Device::CreateBuffer and specifying either D3D11_BIND_VERTEX_BUFFER or D3D11_BIND_INDEX_BUFFER. Here's the code used in BasicLoader:

You typically create a vertex/index buffer pair for every mesh you use in your game. Where and when you load the meshes is up to you. If you have a lot of meshes, you may only want to load some from the disk at specific points in the game, such as during specific, pre-defined loading states. For large meshes, like terrain data, you can stream the vertices from a cache, but that is a more complex procedure and not in the scope of this topic.

Again, know your vertex data format! There are many, many ways to represent vertex data across the tools used to create models. There are also many different ways to represent the input layout of the vertex data to Direct3D, such as triangle lists and strips. For more information about vertex data, read Introduction to Buffers in Direct3D 11 and Primitives.

Next, let's look at loading textures.

Loading textures

The most common asset in a game—and the one that comprises most of the files on disk and in memory—are textures. Like meshes, textures can come in a variety of formats, and you convert them to a format that Direct3D can use when you load them. Textures also come in a wide variety of types and are used to create different effects. MIP levels for textures can be used to improve the look and performance of distance objects; dirt and light maps are used to layer effects and detail atop a base texture; and normal maps are used in per-pixel lighting calculations. In a modern game, a typical scene can potentially have thousands of individual textures, and your code must effectively manage them all!

Also like meshes, there are a number of specific formats that are used to make memory usage for efficient. Since textures can easily consume a large portion of the GPU (and system) memory, they are often compressed in some fashion. You aren't required to use compression on your game's textures, and you can use any compression/decompression algorithm(s) you want as long as you provide the Direct3D shaders with data in a format it can understand (like a Texture2D bitmap).

Direct3D provides support for the DXT texture compression algorithms, although every DXT format may not be supported in the player's graphics hardware. DDS files contain DXT textures (and other texture compression formats as well), and are suffixed with .dds.

A DDS file is a binary file that contains the following information:

  • A DWORD (magic number) containing the four character code value 'DDS ' (0x20534444).

  • A description of the data in the file.

    The data is described with a header description using DDS_HEADER; the pixel format is defined using DDS_PIXELFORMAT. Note that the DDS_HEADER and DDS_PIXELFORMAT structures replace the deprecated DDSURFACEDESC2, DDSCAPS2 and DDPIXELFORMAT DirectDraw 7 structures. DDS_HEADER is the binary equivalent of DDSURFACEDESC2 and DDSCAPS2. DDS_PIXELFORMAT is the binary equivalent of DDPIXELFORMAT.

    If the value of dwFlags in DDS_PIXELFORMAT is set to DDPF_FOURCC and dwFourCC is set to 'DX10' an additional DDS_HEADER_DXT10 structure will be present to accommodate texture arrays or DXGI formats that cannot be expressed as an RGB pixel format such as floating point formats, sRGB formats etc. When the DDS_HEADER_DXT10 structure is present, the entire data description will looks like this.

  • A pointer to an array of bytes that contains the main surface data.

  • A pointer to an array of bytes that contains the remaining surfaces such as; mipmap levels, faces in a cube map, depths in a volume texture. Follow these links for more information about the DDS file layout for a: texture, a cube map, or a volume texture.

Many tools export to the DDS format. If you don't have a tool to export your texture to this format, consider creating one. For more detail on the DDS format and how to work with it in your code, read Programming Guide for DDS. In our example, we'll use DDS.

As with other resource types, you read the data from a file as a stream of bytes. Once your loading task completes, the lambda call runs code (the CreateTexture method) to process the stream of bytes into a format that Direct3D can use. Bartender logiciel mac.

In the previous snippet, the lambda checks to see if the filename has an extension of 'dds'. If it does, you assume that it is a DDS texture. If not, well, use the Windows Imaging Component (WIC) APIs to discover the format and decode the data as a bitmap. Either way, the result is a Texture2D bitmap (or an error).

When this code completes, you have a Texture2D in memory, loaded from an image file. As with meshes, you probably have a lot of them in your game and in any given scene. Consider creating caches for regularly accessed textures per-scene or per-level, rather than loading them all when the game or level starts.

(The CreateDDSTextureFromMemory method called in the above sample can be explored in full in Complete code for DDSTextureLoader.)

Also, individual textures or texture 'skins' may map to specific mesh polygons or surfaces. This mapping data is usually exported by the tool an artist or designer used to create the model and the textures. Make sure that you capture this information as well when you load the exported data, as you will use it map the correct textures to the corresponding surfaces when you perform fragment shading.

Loading shaders

Shaders are compiled High Level Shader Language (HLSL) files that are loaded into memory and invoked at specific stages of the graphics pipeline. The most common and essential shaders are the vertex and pixel shaders, which process the individual vertices of your mesh and the pixels in the scene's viewport(s), respectively. The HLSL code is executed to transform the geometry, apply lighting effects and textures, and perform post-processing on the rendered scene.

A Direct3D game can have a number of different shaders, each one compiled into a separate CSO (Compiled Shader Object, .cso) file. Normally, you don't have so many that you need to load them dynamically, and in most cases, you can simply load them when the game is starting, or on a per-level basis (such as a shader for rain effects).

The code in the BasicLoader class provides a number of overloads for different shaders, including vertex, geometry, pixel, and hull shaders. The code below covers pixel shaders as an example. (You can review the complete code in Complete code for BasicLoader.)

In this example, you use the BasicReaderWriter instance (m_basicReaderWriter Auto tune on garage band 2018. ) to read in the supplied compiled shader object (.cso) file as a byte stream. Once that task completes, the lambda calls ID3D11Device::CreatePixelShader with the byte data loaded from the file. Your callback must set some flag indicating that the load was successful, and your code must check this flag before running the shader.

Vertex shaders are bit more complex. For a vertex shader, you also load a separate input layout that defines the vertex data. The following code can be used to asynchronously load a vertex shader along with a custom vertex input layout. Be sure that the vertex information that you load from your meshes can be correctly represented by this input layout!

C++ game dev resources free

Let's create the input layout before you load the vertex shader.

In this particular layout, each vertex has the following data processed by the vertex shader:

  • A 3D coordinate position (x, y, z) in the model's coordinate space, represented as a trio of 32-bit floating point values.
  • A normal vector for the vertex, also represented as three 32-bit floating point values.
  • A transformed 2D texture coordinate value (u, v) , represented as a pair of 32-bit floating values.

These per-vertex input elements are called HLSL semantics, and they are a set of defined registers used to pass data to and from your compiled shader object. Your pipeline runs the vertex shader once for every vertex in the mesh that you've loaded. The semantics define the input to (and output from) the vertex shader as it runs, and provide this data for your per-vertex computations in your shader's HLSL code.

Now, load the vertex shader object.

In this code, once you've read in the byte data for the vertex shader's CSO file, you create the vertex shader by calling ID3D11Device::CreateVertexShader. After that, you create your input layout for the shader in the same lambda.

Other shader types, such as hull and geometry shaders, can also require specific configuration. Complete code for a variety of shader loading methods is provided in Complete code for BasicLoader and in the Direct3D resource loading sample.

Remarks

At this point, you should understand and be able to create or modify methods for asynchronously loading common game resources and assets, such as meshes, textures, and compiled shaders.

Related topics

  • C++ Basics
  • C++ Object Oriented
  • C++ Advanced
  • C++ Useful Resources
  • Selected Reading

The following resources contain additional information on C++. Please use them to get more in-depth knowledge on this topic.

Useful Links on C++

  • C++ Programming Language Tutorials − C++ Programming Language Tutorials.

  • C++ Programming − This book covers the C++ programming language, its interactions with software design and real life use of the language.

  • Free Country − The Free Country provides free C++ source code and C++ libraries for a number of C++ programming areas including compression, archiving, game programming, the Standard Template Library and GUI programming.

  • C and C++ Users Group − The C and C++ Users Group provides free source code from C++ projects in a variety of programming areas including AI, animation, compilers, databases, debugging, encryption, games, graphics, GUI, language tools, system programming and more.

Useful Books on C++

To enlist your site on this page, please drop an email to contact@tutorialspoint.com