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.