Skip to content

GeometricPrimitive

Chuck Walbourn edited this page Sep 27, 2016 · 47 revisions

This is a helper for drawing simple geometric shapes including texture coordinates and surface normals.

  • Box/Cube
  • Cone
  • Cylinder
  • Dodecahedron
  • Icosahedron
  • Octahedron
  • Sphere (uv or geodesic)
  • Teapot
  • Tetrahedron
  • Torus

Related tutorial: 3D shapes

Header

#include <GeometricPrimitive.h>

Initialization

The GeometryPrimitive class must be created from a factory method..

std::unique_ptr<GeometricPrimitive> shape;
shape = GeometricPrimitive::CreateTeapot();

For exception safety, the factory functions return a std::unique_ptr.

  • CreateBox( const XMFLOAT3& size): Creates a box with a non-uniform size.

  • CreateCone( float diameter = 1, float height = 1, size_t tessellation = 32): Creates a cone of a given height, diameter, and tessellation factor.

  • CreateCube( float size = 1): Creates a cube (also known as a hexahedron) of the given size.

  • CreateCylinder( float height = 1, float diameter = 1, size_t tessellation = 32): Creates a cylinder of given height, diameter, tessellation factor.

  • CreateDodecahedron( float size = 1): Creates a dodecahedron of a given size.

  • CreateGeoSphere( float diameter = 1, size_t tessellation = 3): Creates a geodesic sphere with the given diameter and tessellation factor.

  • CreateIcosahedron( float size = 1): Creates a icosahedron of a given size.

  • CreateOctahedron( float size = 1): Creates a octahedron of a given size.

  • CreateSphere( float diameter = 1, size_t tessellation = 16): Creates a uv-sphere of given diameter with the given tessellation factor.

  • CreateTeapot( float size = 1, size_t tessellation = 8): Creates the Utah Teapot of a given size and tessellation factor.

  • CreateTetrahedron( float size = 1): Creates a tetrahedron of given size.

  • CreateTorus( float diameter = 1, float thickness = 0.333f, size_t tessellation = 32): Creates a torus of given diameter, thickness, and tessellation factor.

Drawing

Before you can draw, you need an effect instance or provide your own root signature, Pipeline State Object (PSO), etc.

RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(),
    m_deviceResources->GetDepthBufferFormat());

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::Opaque,
    CommonStates::DepthDefault,
    CommonStates::CullCounterClockwise,
    rtState);

effect= std::make_unique<BasicEffect>(device,
    EffectFlags::PerPixelLighting
    | EffectFlags::Texture, psd);
effect->EnableDefaultLighting();
effect->SetTexture(resourceDescriptors->GetGpuHandle(Descriptors::MyTexture),
    states->LinearWrap());

The draw transformations are set on the effect before drawing:

effect->SetProjection(projection);
effect->SetView(view);
effect->SetWorld(world);

Then draw the shape using the effect:

ID3D12DescriptorHeap* heaps[] = { resourceDescriptors->Heap(), states->Heap() };
commandList->SetDescriptorHeaps(_countof(heaps), heaps);

effect->Apply(commandList);
shape->Draw(commandList);

Note that GeometricPrimitive::VertexType is an alias for VertexPositionNormalTexture

To provide flexibility, setting the proper descriptor heaps to render with via SetDescriptorHeaps is left to the caller. You can create as many heaps as you wish in your application, but remember that you can have only a single texture descriptor heap and a single sampler descriptor heap active at a given time.

Wireframe rendering

To draw the geometry as a wireframe, use an effect created with this state description:

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::Opaque,
    CommonStates::DepthDefault,
    CommonStates::Wireframe,
    rtState);

Alpha blending

For alpha blending, you typically use CommonStates::DepthRead rather than the normal CommonStates::DepthDefault. To indicate the use of ‘premultiplied’ alpha blending modes, use CommonStates::AlphaBlend.

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::AlphaBlend,
    CommonStates::DepthRead,
    CommonStates::CullCounterClockwise,
    rtState);

If using 'straight' alpha for textures, use CommonStates::NonPremultiplied.

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::NonPremultiplied,
    CommonStates::DepthRead,
    CommonStates::CullCounterClockwise,
    rtState);

Coordinate systems

Geometry is created in a specific winding order, typically using the standard counter-clockwise winding common in graphics (i.e. CommonStates::CullCounterClockwise). The choice of viewing handedness (right-handed vs. left-handed coordinates) is largely a matter of preference and convenience, but it impacts how the geometry is built.

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::Opaque,
    CommonStates::DepthDefault,
    CommonStates::CullCounterClockwise,
    rtState);

These geometric primitives (based on the XNA Game Studio conventions) use right-handed coordinates. They can be used with left-handed coordinates by setting the rhcoords parameter on the factory methods to 'false' to reverse the winding ordering (the parameter defaults to 'true').

For a left-handed view system:

shape = GeometricPrimitive::CreateTeapot( 1.f, 8, false ) );

Alternatively, you can instead reverse the winding order for the culling although this can potentially have the ‘flipped in U’ texture problem.

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::Opaque,
    CommonStates::DepthDefault,
    CommonStates::CullClockwise,
    rtState);

Note: Using the wrong value for rhcoords for your viewing setup will result in the objects looking 'inside out'.

Lastly you can choose to render with back-face culling disabled, but this generally results in less performance.

EffectPipelineStateDescription psd(
    &GeometricPrimitive::VertexType::InputLayout,
    CommonStates::Opaque,
    CommonStates::DepthDefault,
    CommonStates::CullNone,
    rtState);

Inside vs. Outside

These geometric primitives are intended for view from the 'outside' for efficient back-face culling. However, both spheres and boxes are commonly used to form 'skyboxes' for backgrounds. To support this, you set the rhcoords parameter backwards for your view coordinates, and then set invertn to true.

For a right-handed view system:

sky = GeometricPrimitive::CreateBox( XMFLOAT3(10,10,10), false, true);

sky = GeometricPrimitive::CreateSphere( 100.f, false, true);

For a left-handed view system:

sky = GeometricPrimitive::CreateBox( XMFLOAT3(10,10,10), true, true);

sky = GeometricPrimitive::CreateSphere( 100.f, true, true);

Custom geometry

There are equivalent static methods for each of the factory methods that return the vertex and index buffer data as std::vector. These values can be modified, and then used to create a customized geometric primitive or drawn through some other mechanism.

std::vector<VertexPositionNormalTexture> vertices;
std::vector<uint16_t> indices;
GeometricPrimitive::CreateBox( vertices, indices,
    XMFLOAT3(1.f/2.f, 2.f/2.f, 3.f/2.f) );

// Tile the texture in a 5x5 grid
for( auto it = vertices.begin(); it != vertices.end(); ++it )
{
    it->textureCoordinate.x *= 5.f;
    it->textureCoordinate.y *= 5.f;
}

customBox = GeometricPrimitive::CreateCustom( vertices, indices ) );

Threading model

The GeometricPrimitive is tied to a device, but not a command-list. This means that creation/loading is ‘free threaded’. Drawing can be done on any command-list, but keep in mind command-lists are not ‘free threaded’.

Work Submission in Direct3D 12

State management

When Draw is called, it sets the Primitive Topology, Vertex Buffer in slot 0, and Index Buffer to use.

The GeometricPrimitive class assumes you've already set the Render Target view, Depth Stencil view, Viewport, ScissorRects, Descriptor Heaps (for textures and samplers), root signature, and Pipeline State Object (PSO) to the command-list provided to Draw.

Remark

In the DirectX 11 version of DirectX Tool Kit, the geometric primitive automatically creates a BasicEffect to simplify usage. With DirectX 12, you must always provide the effect yourself.

Tetrahedron, Cube/Hexahedron, Octahedron, Dodecahedron, and Icosahedron comprise the five Platonic solid. The Utah Teapot is sometimes referred to as the "Sixth Platonic solid" due to its prevalence in rendering sample images.

For Use

  • Universal Windows Platform apps
  • Windows desktop apps
  • Windows 11
  • Windows 10
  • Xbox One
  • Xbox Series X|S

For Development

  • Visual Studio 2022
  • Visual Studio 2019 (16.11)
  • clang/LLVM v12 - v18
  • MinGW 12.2, 13.2
  • CMake 3.20

Related Projects

DirectX Tool Kit for DirectX 11

DirectXMesh

DirectXTex

DirectXMath

Tools

Test Suite

Model Viewer

Content Exporter

DxCapsViewer

See also

DirectX Landing Page

Clone this wiki locally