Render Mesh Asset Interfaces

Apex render data is stored in a vertex buffer accessible through the VertexBuffer interface. This vertex buffer supports a wide variety of attributes, and it also supports a variety of formats for each attribute. A format descriptor for the entire buffer is accessible through the VertexFormat interface.

Although every vertex attribute (for example position, normal, etc.) may be given any format (for example float[3], byte[3], etc.), there are some restrictions imposed by the framework and individual modules. There are two overall (framework) restrictions:

  • vertex positions must be stored in the FLOAT3 format
  • vertex color must be stored in the R8G8B8A8 or B8G8R8A8 (PxU8[4]) formats

See the individual module documentation for further per-module restrictions.

One authors an apex render mesh by creating an authoring object with the ApexSDK::createRenderMeshAsset function. This object uses the RenderMeshAssetAuthoring interface.

RenderMeshAssetAuthoring Interface

Apex render mesh assets are built by filling in a descriptor, RenderMeshAssetAuthoring::MeshDesc. See RenderMeshAsset.h. This descriptor is simply an array of “submesh” descriptors, of type RenderMeshAssetAuthoring::SubmeshDesc. Submeshes correspond to individual materials, or shaders, or other render state. The idea is that a submesh can be drawn (ideally) in one draw call.

RenderMeshAssetAuthoring::SubmeshDesc

The SubmeshDesc is set up so that you can point its fields directly to your mesh data, with minimal (or no) need to convert it into an intermediate format.

Vertex Buffers

The SubmeshDesc contains contains an array of VertexBuffer objects, which just contain format descriptors and pointers to your data. You set these pointers using RenderMeshAssetAuthoring::VertexBuffer::setSemanticData(...):

PX_INLINE void setSemanticData(RenderVertexSemantic::Enum semantic, const void *data, physx::PxU32 stride, RenderDataFormat::Enum format, RenderDataFormat::Enum srcFormat = RenderDataFormat::UNSPECIFIED)

The first argument, semantic, is the data channel you are setting. For example, this may be the vertex position, normal, tangent, uv coordinate, etc. The possible “standard semantics” are given below.

Semantic (RenderVertexSemantic::Enum) Meaning
POSITION Position of vertex
NORMAL Normal at vertex
TANGENT Tangent at vertex
BINORMAL Binormal at vertex
COLOR Color at vertex
TEXCOORD0 Texture coord 0 of vertex
TEXCOORD1 Texture coord 1 of vertex
TEXCOORD2 Texture coord 2 of vertex
TEXCOORD3 Texture coord 3 of vertex
BONE_INDEX Bone index of vertex
BONE_WEIGHT Bone weight of vertex

Only these semantics may be set with the setSemanticData function, and only one data channel per semantic is allowed. The data buffer pointer is passed in as a void* pointer in data. The stride field gives the byte difference between one data element and the next. The format field is the format you wish to store the data in, in the created render mesh. If this is same format as the source data (pointed to by data), then you may leave the last parameter at its default. If the source data is a different format, you must specify this format in the last parameter.

The allowed formats are given in RenderDataFormat::Enum (see RenderDataFormat.h). These formats are shown below.

Format (RenderDataFormat::Enum) Meaning
UNSPECIFIED No format (semantic not used)
UBYTE1 One unsigned 8-bit integer (PxU8[1])
UBYTE2 Two unsigned 8-bit integers (PxU8[2])
UBYTE3 Three unsigned 8-bit integers (PxU8[3])
UBYTE4 Four unsigned 8-bit integers (PxU8[4])
USHORT1 One unsigned 16-bit integer (PxU16[1])
USHORT2 Two unsigned 16-bit integers (PxU16[2])
USHORT3 Three unsigned 16-bit integers (PxU16[3])
USHORT4 Four unsigned 16-bit integers (PxU16[4])
SHORT1 One signed 16-bit integer (PxI16[1])
SHORT2 Two signed 16-bit integers (PxI16[2])
SHORT3 Three signed 16-bit integers (PxI16[3])
SHORT4 Four signed 16-bit integers (PxI16[4])
UINT1 One unsigned integer (PxU32[1])
UINT2 Two unsigned integers (PxU32[2])
UINT3 Three unsigned integers (PxU32[3])
UINT4 Four unsigned integers (PxU32[4])
R8G8B8A8 Four unsigned bytes (PxU8[4]) representing red, green, blue, alpha
B8G8R8A8 Four unsigned bytes (PxU8[4]) representing blue, green, red, alpha
R32G32B32A32_FLOAT Four floats (PxF32[4]) representing red, green, blue, alpha
B32G32R32A32_FLOAT Four floats (PxF32[4]) representing blue, green, red, alpha
BYTE_UNORM1 One unsigned normalized value in the range [0,1], packed into 8 bits (PxU8[1])
BYTE_UNORM2 Two unsigned normalized value in the range [0,1], each packed into 8 bits (PxU8[2])
BYTE_UNORM3 Three unsigned normalized value in the range [0,1], each packed into bits (PxU8[3])
BYTE_UNORM4 Four unsigned normalized value in the range [0,1], each packed into 8 bits (PxU8[4])
SHORT_UNORM1 One unsigned normalized value in the range [0,1], packed into 16 bits (PxU16[1])
SHORT_UNORM2 Two unsigned normalized value in the range [0,1], each packed into 16 bits (PxU16[2])
SHORT_UNORM3 Three unsigned normalized value in the range [0,1], each packed into 16 bits (PxU16[3])
SHORT_UNORM4 Four unsigned normalized value in the range [0,1], each packed into 16 bits (PxU16[4])
BYTE_SNORM1 One signed normalized value in the range [-1,1], packed into 8 bits (PxU8[1])
BYTE_SNORM2 Two signed normalized value in the range [-1,1], each packed into 8 bits (PxU8[2])
BYTE_SNORM3 Three signed normalized value in the range [-1,1], each packed into bits (PxU8[3])
BYTE_SNORM4 Four signed normalized value in the range [-1,1], each packed into 8 bits (PxU8[4])
SHORT_SNORM1 One signed normalized value in the range [-1,1], packed into 16 bits (PxU16[1])
SHORT_SNORM2 Two signed normalized value in the range [-1,1], each packed into 16 bits (PxU16[2])
SHORT_SNORM3 Three signed normalized value in the range [-1,1], each packed into 16 bits (PxU16[3])
SHORT_SNORM4 Four signed normalized value in the range [-1,1], each packed into 16 bits (PxU16[4])
HALF1 One 16-bit floating point value
HALF2 Two 16-bit floating point values
HALF3 Three 16-bit floating point values
HALF4 Four 16-bit floating point values
FLOAT1 One 32-bit floating point value
FLOAT2 Two 32-bit floating point values
FLOAT3 Three 32-bit floating point values
FLOAT4 Four 32-bit floating point values
FLOAT3x4 A 3x4 matrix (see PxMat34)
FLOAT3x3 A 3x3 matrix (see PxMat33)
FLOAT4_QUAT A quaternion (see PxQuat)
BYTE_SNORM4_QUATXYZW A normalized quaternion with signed byte elements, X,Y,Z,W format (PxU8[4])
SHORT_SNORM4_QUATXYZW A normalized quaternion with signed short elements, X,Y,Z,W format (PxU16[4])

Source and destination formats may be different only if a conversion is defined between the formats. Currently, these conversions are defined:

  • BYTE_SNORM1 <=> FLOAT1
  • BYTE_SNORM2 <=> FLOAT2
  • BYTE_SNORM3 <=> FLOAT3
  • BYTE_SNORM4 <=> FLOAT4
  • SHORT_SNORM1 <=> FLOAT1
  • SHORT_SNORM2 <=> FLOAT2
  • SHORT_SNORM3 <=> FLOAT3
  • SHORT_SNORM4 <=> FLOAT4
  • BYTE_SNORM4_QUATXYZW <=> FLOAT4_QUAT
  • SHORT_SNORM4_QUATXYZW <=> FLOAT4_QUAT
  • USHORT1 <=> UINT1
  • USHORT2 <=> UINT2
  • USHORT3 <=> UINT3
  • USHORT4 <=> UINT4
  • R8G8B8A8 <=> B8G8R8A8
  • R8G8B8A8 <=> R32G32B32A32_FLOAT
  • R8G8B8A8 <=> B32G32R32A32_FLOAT
  • B8G8R8A8 <=> R32G32B32A32_FLOAT
  • B8G8R8A8 <=> B32G32R32A32_FLOAT
  • R32G32B32A32_FLOAT <=> B32G32R32A32_FLOAT

If you wish to supply other data channels, you may create an array of custom buffer descriptors (RenderMeshAssetAuthoring::VertexBuffer::RenderSemanticData) and pass them into RenderMeshAssetAuthoring::VertexBuffer::setCustomSemanticData(...).

When possible, however, use the standard semantics so that APEX modules which might rely on the data can interpret it correctly. For example, positions should always use the RenderVertexSemantic::POSITION semantic, as many modules rely on vertex positions for various calculations.

Index Buffers

In addition, you supply the mesh’s index buffer in the SubmeshDesc. Currently only triangle primitives (no strips or fans) are supported. If you partition your index buffer(s) into disjoint parts, you can use the SubmeshDesc’s m_partIndices array to specify this partition. The RenderMesh interface allows the user to set visibility on part subsets of the mesh. Note: it is required that the vertex buffer also be similarly partitioned by parts, but this is not enforced in the user’s data. The authoring function will sort the APEX render mesh’s vertex buffer to meet this requirement, however. This means that the final vertex indices may not correspond to your original indices. In order to map the vertices back to your original ordering, you may create a mapping buffer as described below.

The RenderMeshAssetAuthoring::createRenderMesh(...) function takes the MeshDesc descriptor and builds an APEX render mesh asset from it. Its second parameter is the createMappingInformation bool. If this is set to TRUE, then a custom buffer named “VERTEX_ORIGINAL_INDEX” (see VertexBuffer Interface) will be created which is is the original index in the MeshDesc for each vertex. The only time the vertices will be rearranged is when they are made into contiguous parts. There is no further cooking of the mesh data, for example to reduce the vertex buffer by eliminating redundant vertices.

If you wish to reduce your vertex buffer by eliminating redundant vertices, the RenderMeshAssetAuthoring API provides a helper function to do this called createReductionMap(...). It only gives you a map by which you may reduce your vertex data. It will not modify your vertex data for you.

Once you have called RenderMeshAssetAuthoring::createRenderMesh, this authoring class may be used to create an RenderMeshAsset using the standard authorable asset functions (see Loading Assets).

Once you have created an APEX render mesh, you may access it through the RenderMeshAsset interface. This interface gives you the mesh broken into submeshes via the RenderMeshAsset::getSubmesh method. This method returns an RenderSubmesh interface. Through that, you can access the mesh’s vertex buffer using RenderSubmesh::getVertexBuffer, which returns an VertexBuffer interface.

VertexBuffer Interface

The VertexBuffer interface gives you access to the render data, and also allows conversion between render data formats. Every RenderSubmesh has this interface.

The number of vertices is returned by this function:

virtual physx::PxU32            getVertexCount() const = 0;

The format of the vertices is returned by this function:

virtual const VertexFormat&   getFormat() const = 0;

This returns an VertexFormat interface, described below, in VertexFormat Interface .

The VertexFormat interface will give you a buffer index for any data channel (semantic or custom data) in the buffer. You may then use that index to access the buffer, using

virtual const void*             getBuffer( physx::PxU32 bufferIndex ) const = 0;

The data is returned as a void*, which you must cast appropriately based upon the data format (also obtained from the VertexFormat interface). Both of these operations may be done at once using

virtual const void*             getBufferAndFormat( RenderDataFormat::Enum& format, physx::PxU32 bufferIndex ) const = 0;

Finally, there is a utility which will fill a user buffer with render buffer data, and even convert between the render data format and a destination format specified by the user, if that conversion exists. You may also specify a subset of the buffer to copy, using the startVertexIndex and elementCount fields.

virtual bool                    getBufferData( void* buffer, physx::RenderDataFormat::Enum bufferFormat, physx::PxU32 bufferIndex,
                                               physx::PxU32 startVertexIndex, physx::PxU32 elementCount ) const = 0;

Note, regardless of the value of startVertexIndex, the buffer will always be filled starting at the buffer’s beginning. Also note, you may find the size of an element in any format using the function

RenderDataFormat::getFormatDataSize( RenderDataFormat::Enum format );

VertexFormat Interface

This interface accompanies the VertexBuffer interface and may be accessed through VertexBuffer::getFormat.

The various semantics and custom buffers formats, as well as their buffer index (used to retrieve the buffer data using VertexBuffer::getBuffer), are all looked up by a unique buffer ID. This ID is a hash of the buffer name. For a custom buffer, this name is specified by the user. For a standard semantic (RenderVertexSemantic::Enum), this name may be looked up using

virtual const char*                 getSemanticName( RenderVertexSemantic::Enum semantic ) const = 0;

Whatever the name, the buffer ID may be looked up using

virtual BufferID                    getID( const char* name ) const = 0;

In addition there is the shortcut function for semantics,

virtual BufferID                    getSemanticID( RenderVertexSemantic::Enum semantic ) const = 0;

This buffer ID will not change, and so may be serialized, cached, etc. To find a buffer index use

virtual physx::PxI32                getBufferIndexFromID( BufferID id ) const = 0;

All of the format accessors, as well as the VertexBuffer accessors, use this index. Note, this index is volitile. If you add or remove semantics or custom buffers, the index may change.

There are basically three pieces of information associated with each buffer: format, access type, and serialization.

The buffer’s format is enumerated using RenderDataFormat::Enum, and is the data layout. This is accessed using:

virtual RenderDataFormat::Enum    getBufferFormat( physx::PxU32 index ) const = 0;

The buffer’s access type is a hint regarding how the data will be accessed. There are three types, enumerated by RenderDataAccess::Enum. These are STATIC, DYNAMIC, and STREAMING. STATIC buffers are only sent to the application once. DYNAMIC and STREAMING buffers are sent every frame.

Use

virtual RenderDataAccess::Enum    getBufferAccess( physx::PxU32 index ) const = 0;

for the access type.

Finally, you can query a buffer to see whether it will get serialized, using

virtual bool                        getBufferSerialize( physx::PxU32 index ) const = 0;

Corresponding set functions, as well as the rest of the VertexFormat API is documented in the APEX Framework API section.