DirectX 12 / PRIMARY BACKEND

Mesh loading & GPU buffers

Extend the indexed cube into an import pipeline with validated vertex data, material slots, bounds, and GPU-local storage.

Begin with geometry that already draws#

Sample::CreateGeometry creates twenty-four cube vertices and thirty-six indices for the texture target. Each face has its own UVs and tint. This function makes the draw contract visible before a model importer introduces multiple files, coordinate systems, and material conventions. Build and run qubic_texture using the setup commands. This chapter is a reference extension, not a claim that the teaching executable already imports arbitrary model formats. Imported data must ultimately produce the same vertex/index views and a compatible pipeline.

Define the import result#

MeshImportData owns CPU vertices, indices, submesh ranges, material references, bounds, and optional skeleton/skin data. It is produced on a worker thread. GPU MeshAsset owns default resources and their views after upload completion. AssetStore publishes a handle when the asset is ready.

Import fieldValidationConsumer
PositionFinite coordinates and chosen unitsGeometry, bounds, collision authoring
IndexWithin vertex count; format fitsIndexed draws
Normal/tangentCorrect convention and valid lengthLighting and normal mapping
UVExpected channel and orientationTexture sampling
Submesh rangeWithin index storageMaterial-specific draw packets
Joint weightsValid joints, normalized totalSkinning shader

Coordinate conversion happens once in import. A left/right-handed conversion can reverse winding and alter tangent handedness. Baking the same correction into both import and rendering transforms the model twice.

Copy static geometry into default resources#

A default buffer starts in COPY_DEST. An upload buffer holds validated bytes. CopyBufferRegion records the transfer. Transition the destination to VERTEX_AND_CONSTANT_BUFFER or INDEX_BUFFER. Keep upload buffers alive until the submission fence completes, then create the ready MeshAsset views.

cpp · REFERENCE EXCERPT
// All objects are owned by a pending mesh-upload record.
list->CopyBufferRegion(gpuVertices.Get(), 0, stagingVertices.Get(), 0, vertexBytes);
Transition(list.Get(), gpuVertices.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST,
    D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);

This is an architectural excerpt. Transition is fully defined in the native sample. gpuVertices and stagingVertices are created with BufferDesc using DEFAULT/COPY_DEST and UPLOAD/GENERIC_READ respectively; vertexBytes is the validated CPU byte count. A copy-queue implementation needs the queue synchronization and final usage transition on appropriate queues rather than directly copying this direct-queue sequence.

Bind submeshes and instances#

A submesh gives firstIndex and indexCount with a material slot. DrawIndexedInstanced uses those values against the shared index view. If imported indices are local to a submesh, baseVertex must be handled consistently. A wrong base vertex can read valid memory while producing a wildly incorrect shape. Instancing shares geometry across world transforms. Instance data belongs in a frame-safe buffer indexed by instance ID or an indirect-draw contract. The material binding strategy determines whether instances can share one draw; batching unrelated pipeline states together is not a valid optimization.

Bounds and visibility#

Compute local bounds from imported vertices, then conservatively transform them for visibility tests. A skinned character may need expanded or dynamically evaluated bounds; bind-pose bounds alone can clip moving limbs. LOD assets preserve material and animation compatibility where required.

INTERACTIVE ILLUSTRATIONSIMULATION / BROWSER

Spend work on what you can see This illustration uses canvas. The explanation below describes the same process.

Change a control to inspect the result. Values describe the simulation, not native engine benchmarks. Open full lab ↗

Build and troubleshoot the extension#

Add your importer dependency in CMake and implement MeshImportData without coupling it to DX12 pointers. Feed one known triangle through import before adding complex assets. Compare vertex count, index count, bounds, UVs, and winding against the built-in cube. Then add default-buffer upload and its pending lifetime record. A torn model may have an incorrect index format or stride. A mirrored model may have an incomplete coordinate conversion. Wrong materials may have lost submesh slot mapping. An animated model that vanishes can have stale bounds. Read materials and animation for the additional contracts rather than hiding them inside an unexplained LoadModel call.

Search titles and full article text.