Use MeshData struct for creating Meshes.

Consolidate all Vertex and Index buffer creation inside the
Mesh class. Users now set data pointers in the MeshData
from which the Mesh class will create the filament buffers.

PiperOrigin-RevId: 895871213
Change-Id: Ie086fcb5c31adf90e54cba168b03501e21720726
This commit is contained in:
Haroon Qureshi
2026-04-07 06:40:51 -07:00
committed by Copybara-Service
parent 9f3babd60c
commit a3c9115d1e
10 changed files with 579 additions and 1083 deletions
@@ -25,7 +25,6 @@
#include <filament/IndexBuffer.h>
#include <filament/VertexBuffer.h>
#include <backend/BufferDescriptor.h>
#include "third_party/filament/libs/filabridge/include/filament/MaterialEnums.h"
#include <math/TVecHelpers.h>
#include <math/vec3.h>
#include <math/vec4.h>
@@ -89,6 +88,17 @@ int VertexAttributeTypeSize(const VertexAttribute& attrib) {
}
}
// Fills an index buffer with a basic incrementing sequence.
template <typename T>
int FillSequence(std::byte* buffer, std::size_t num_bytes) {
const T num = num_bytes / sizeof(T);
T* ptr = reinterpret_cast<T*>(buffer);
for (T i = 0; i < num; ++i) {
ptr[i] = i;
}
return num;
}
// Initializes the MeshData to default values.
void DefaultMeshData(MeshData* data) {
std::memset(data, 0, sizeof(MeshData));
@@ -140,11 +150,7 @@ void Mesh::BuildVertexBuffer(const MeshData& data) {
const VertexAttribute* positions = nullptr;
const VertexAttribute* normals = nullptr;
const VertexAttribute* tangents = nullptr;
// Calculate the stride of the vertex buffer.
int stride = 0;
for (int i = 0; i < data.nattributes; ++i) {
stride += VertexAttributeTypeSize(data.attributes[i]);
if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_POSITION) {
positions = &data.attributes[i];
} else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_NORMAL) {
@@ -153,7 +159,6 @@ void Mesh::BuildVertexBuffer(const MeshData& data) {
tangents = &data.attributes[i];
}
}
if (!positions) {
mju_error("MeshData has no positions.");
}
@@ -171,47 +176,66 @@ void Mesh::BuildVertexBuffer(const MeshData& data) {
// Build the vertex buffer.
filament::VertexBuffer::Builder vb_builder;
vb_builder.bufferCount(data.interleaved ? 1 : data.nattributes);
vb_builder.vertexCount(data.nvertices);
int offset = 0;
for (int i = 0; i < data.nattributes; ++i) {
const VertexAttribute& attrib = data.attributes[i];
const filament::VertexAttribute usage = GetUsage(attrib);
const filament::VertexBuffer::AttributeType type = GetType(attrib);
if (data.interleaved) {
vb_builder.attribute(usage, 0, type, offset, stride);
} else {
vb_builder.attribute(usage, i, type);
}
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
offset += VertexAttributeTypeSize(attrib);
}
vertex_buffer_ = vb_builder.build(*engine_);
if (data.interleaved) {
const VertexAttribute& attrib = data.attributes[0];
const size_t nbytes = data.nvertices * stride;
filament::backend::BufferDescriptor desc(attrib.bytes, nbytes, callback,
this);
vertex_buffer_->setBufferAt(*engine_, 0, std::move(desc));
} else {
// For an interleaved vertex buffer, we will create a single buffer which
// contains the data in the order specified by the attributes array,
// starting from the first attribute's payload.
vb_builder.bufferCount(1);
int total_vertex_size = 0;
for (int i = 0; i < data.nattributes; ++i) {
total_vertex_size += VertexAttributeTypeSize(data.attributes[i]);
}
const void* bytes = data.attributes[0].bytes;
const size_t nbytes = data.nvertices * total_vertex_size;
// We assume the buffer is tightly packed with no padding between
// attributes. As such, the stride is equal to the total vertex size and
// each offset is the sum of the sizes of the preceding attributes.
int offset = 0;
for (int i = 0; i < data.nattributes; ++i) {
const VertexAttribute& attrib = data.attributes[i];
const size_t nbytes = data.nvertices * VertexAttributeTypeSize(attrib);
if (attrib.usage == mjVERTEX_ATTRIBUTE_NORMAL) {
const float4* orientations =
BuildOrientationsFromNormals(data.nvertices, attrib);
filament::backend::BufferDescriptor desc(orientations, nbytes, callback,
this);
vertex_buffer_->setBufferAt(*engine_, i, std::move(desc));
} else {
filament::backend::BufferDescriptor desc(attrib.bytes, nbytes, callback,
this);
vertex_buffer_->setBufferAt(*engine_, i, std::move(desc));
const filament::VertexAttribute usage = GetUsage(attrib);
filament::VertexBuffer::AttributeType type = GetType(attrib);
vb_builder.attribute(usage, 0, type, offset, total_vertex_size);
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
offset += VertexAttributeTypeSize(attrib);
}
vertex_buffer_ = vb_builder.build(*engine_);
vertex_buffer_->setBufferAt(*engine_, 0, {bytes, nbytes, callback, this});
} else {
// For a non-interleaved vertex buffer, we assign a separate buffer to each
// attribute.
vb_builder.bufferCount(data.nattributes);
for (int i = 0; i < data.nattributes; ++i) {
const VertexAttribute& attrib = data.attributes[i];
const filament::VertexAttribute usage = GetUsage(attrib);
filament::VertexBuffer::AttributeType type = GetType(attrib);
if (attrib.usage == mjVERTEX_ATTRIBUTE_NORMAL) {
// We will replace normals with orientations.
type = filament::VertexBuffer::AttributeType::FLOAT4;
}
vb_builder.attribute(usage, i, type);
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
}
vertex_buffer_ = vb_builder.build(*engine_);
// Assign the individual data buffers.
for (int i = 0; i < data.nattributes; ++i) {
const VertexAttribute& attrib = data.attributes[i];
const void* bytes = attrib.bytes;
size_t nbytes = data.nvertices * VertexAttributeTypeSize(attrib);
if (attrib.usage == mjVERTEX_ATTRIBUTE_NORMAL) {
// Replace normals with orientations.
nbytes = data.nvertices * sizeof(float4);
bytes = BuildOrientationsFromNormals(data.nvertices, attrib);
}
vertex_buffer_->setBufferAt(*engine_, i, {bytes, nbytes, callback, this});
}
}
}
@@ -318,15 +342,4 @@ bool Mesh::HasBounds() const {
filament::Box Mesh::GetBounds() const {
return bounds_.value();
}
filament::backend::BufferDescriptor CreateBufferDescriptor(
std::size_t num_bytes, const FillBufferFn& fill) {
std::byte* bytes = new std::byte[num_bytes];
fill(bytes, num_bytes);
const auto callback = [](void* buffer, size_t size, void* user) {
auto* ptr = reinterpret_cast<std::byte*>(user);
delete[] ptr;
};
return filament::backend::BufferDescriptor(bytes, num_bytes, callback, bytes);
}
} // namespace mujoco
@@ -16,14 +16,11 @@
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_BUFFER_UTIL_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <type_traits>
#include <vector>
#include <backend/BufferDescriptor.h>
#include <filament/Box.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
@@ -139,18 +136,6 @@ class Mesh {
// Creates a Mesh from the given MeshData.
Mesh(filament::Engine* engine, const MeshData& data);
// Create a Mesh directly from filament objects. Internal use only.
Mesh(filament::Engine* engine, filament::IndexBuffer* index_buffer,
filament::VertexBuffer* vertex_buffer,
std::optional<filament::Box> bounds = std::nullopt,
filament::RenderableManager::PrimitiveType type =
filament::RenderableManager::PrimitiveType::TRIANGLES)
: engine_(engine),
index_buffer_(index_buffer),
vertex_buffer_(vertex_buffer),
type_(type),
bounds_(bounds) {}
~Mesh();
// Returns the filament IndexBuffer for the mesh.
@@ -192,114 +177,6 @@ class Mesh {
using MeshPtr = std::unique_ptr<Mesh>;
// Function that fills in the given buffer with actual data.
using FillBufferFn = std::function<void(std::byte*, std::size_t)>;
// Creates and populates a BufferDescriptor (for vertex and index buffers).
filament::backend::BufferDescriptor CreateBufferDescriptor(
std::size_t num_bytes, const FillBufferFn& fill);
// Creates a filament::VertexBuffer based on the VertexType. The fill function
// will be used to populate the buffer.
template <typename VertexType>
filament::VertexBuffer* CreateVertexBuffer(filament::Engine* engine,
std::size_t num_vertices,
const FillBufferFn& fill) {
int vertex_size = 0;
if constexpr (VertexType::kHasPosition) {
vertex_size += sizeof(VertexType::position);
}
if constexpr (VertexType::kHasPosition2d) {
vertex_size += sizeof(VertexType::position);
}
if constexpr (VertexType::kHasOrientation) {
vertex_size += sizeof(VertexType::orientation);
}
if constexpr (VertexType::kHasUv) {
vertex_size += sizeof(VertexType::uv);
}
if constexpr (VertexType::kHasColor) {
vertex_size += sizeof(VertexType::color);
}
auto builder = filament::VertexBuffer::Builder();
builder.bufferCount(1);
builder.vertexCount(num_vertices);
int offset = 0;
if constexpr (VertexType::kHasPosition) {
builder.attribute(filament::VertexAttribute::POSITION, 0,
filament::VertexBuffer::AttributeType::FLOAT3, offset,
vertex_size);
offset += sizeof(VertexType::position);
}
if constexpr (VertexType::kHasPosition2d) {
builder.attribute(filament::VertexAttribute::POSITION, 0,
filament::VertexBuffer::AttributeType::FLOAT2, offset,
vertex_size);
offset += sizeof(VertexType::position);
}
if constexpr (VertexType::kHasOrientation) {
builder.attribute(filament::VertexAttribute::TANGENTS, 0,
filament::VertexBuffer::AttributeType::FLOAT4, offset,
vertex_size);
offset += sizeof(VertexType::orientation);
}
if constexpr (VertexType::kHasUv) {
builder.attribute(filament::VertexAttribute::UV0, 0,
filament::VertexBuffer::AttributeType::FLOAT2, offset,
vertex_size);
offset += sizeof(VertexType::uv);
}
if constexpr (VertexType::kHasColor) {
builder.attribute(filament::VertexAttribute::COLOR, 0,
filament::VertexBuffer::AttributeType::UBYTE4, offset,
vertex_size);
builder.normalized(filament::VertexAttribute::COLOR);
offset += sizeof(VertexType::color);
}
auto vb = builder.build(*engine);
const std::size_t buffer_size = num_vertices * vertex_size;
vb->setBufferAt(*engine, 0, CreateBufferDescriptor(buffer_size, fill));
return vb;
}
// Creates a filament::IndexBuffer. The IndexType should be either uin16_t or
// uint32_t. The fill function will be used to populate the buffer.
template <typename IndexType>
filament::IndexBuffer* CreateIndexBuffer(filament::Engine* engine,
std::size_t num_indices,
const FillBufferFn& fill) {
static_assert(std::is_same<IndexType, uint16_t>::value ||
std::is_same<IndexType, uint32_t>::value);
constexpr auto type = std::is_same<IndexType, uint16_t>::value
? filament::IndexBuffer::IndexType::USHORT
: filament::IndexBuffer::IndexType::UINT;
auto builder = filament::IndexBuffer::Builder();
builder.bufferType(type);
builder.indexCount(num_indices);
auto ib = builder.build(*engine);
const std::size_t buffer_size = num_indices * sizeof(IndexType);
ib->setBuffer(*engine, CreateBufferDescriptor(buffer_size, fill));
return ib;
}
// Fills an index buffer with a basic incrementing sequence.
template <typename T>
int FillSequence(std::byte* buffer, std::size_t num_bytes) {
const T num = num_bytes / sizeof(T);
T* ptr = reinterpret_cast<T*>(buffer);
for (T i = 0; i < num; ++i) {
ptr[i] = i;
}
return num;
}
} // namespace mujoco
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_BUFFER_UTIL_H_
File diff suppressed because it is too large Load Diff
+22 -73
View File
@@ -14,27 +14,19 @@
#include "experimental/filament/filament/geom_util.h"
#include <cfloat>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <span>
#include <filament/Box.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/VertexBuffer.h>
#include <math/vec3.h>
#include <mujoco/mujoco.h>
#include "experimental/filament/filament/buffer_util.h"
#include "experimental/filament/filament/math_util.h"
#include "experimental/filament/filament/vertex_util.h"
namespace mujoco {
using filament::math::float3;
static std::span<const float> GetPositions(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
@@ -106,64 +98,6 @@ static std::span<const int> GetIndices(const mjModel* model,
}
}
template <typename T>
static void FillVertices(std::byte* buffer, std::size_t len,
std::span<const float> positions,
std::span<const float> normals,
std::span<const float> uvs,
float3* vmin,
float3* vmax) {
const int num_vertices = len / sizeof(T);
T* ptr = reinterpret_cast<T*>(buffer);
for (int i = 0; i < num_vertices; ++i) {
ptr->position = ReadFloat3(positions.data(), i);
*vmin = min(*vmin, ptr->position);
*vmax = max(*vmax, ptr->position);
ptr->orientation = CalculateOrientation(ReadFloat3(normals.data(), i));
if constexpr (T::kHasUv) {
ptr->uv.x = uvs[i * 2];
ptr->uv.y = uvs[i * 2 + 1];
}
++ptr;
}
}
static filament::VertexBuffer* BuildVertexBuffer(
filament::Engine* engine, std::span<const float> positions,
std::span<const float> normals, std::span<const float> uvs, float3* vmin,
float3* vmax) {
const int num_vertices = positions.size() / 3;
if (uvs.data() != nullptr) {
using VertexType = VertexWithUv;
auto fill = [&](std::byte* buffer, std::size_t len) {
FillVertices<VertexType>(buffer, len, positions, normals, uvs, vmin,
vmax);
};
return CreateVertexBuffer<VertexType>(engine, num_vertices, fill);
} else {
using VertexType = VertexNoUv;
auto fill = [&](std::byte* buffer, std::size_t len) {
FillVertices<VertexType>(buffer, len, positions, normals, uvs, vmin,
vmax);
};
return CreateVertexBuffer<VertexType>(engine, num_vertices, fill);
}
}
static filament::IndexBuffer* BuildIndexBuffer(filament::Engine* engine,
std::span<const int> indices,
int num_indices) {
if (indices.data() == nullptr) {
auto fill_indices = FillSequence<uint32_t>;
return CreateIndexBuffer<uint32_t>(engine, num_indices, fill_indices);
} else {
auto fill_indices = [&](std::byte* buffer, std::size_t len) {
std::memcpy(buffer, indices.data(), len);
};
return CreateIndexBuffer<uint32_t>(engine, indices.size(), fill_indices);
}
}
MeshPtr CreateGeomBuffers(filament::Engine* engine, const mjModel* model,
const mjvScene* scene, const mjvGeom& geom) {
auto positions = GetPositions(model, scene, geom);
@@ -176,13 +110,28 @@ MeshPtr CreateGeomBuffers(filament::Engine* engine, const mjModel* model,
num_indices = 3 * scene->flexfaceused[geom.objid];
}
float3 vmin = {FLT_MAX, FLT_MAX, FLT_MAX};
float3 vmax = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
auto vertex_buffer = BuildVertexBuffer(engine, positions, normals, uvs, &vmin, &vmax);
auto index_buffer = BuildIndexBuffer(engine, indices, num_indices);
filament::Box bounds;
bounds.set(vmin, vmax);
return std::make_unique<Mesh>(engine, index_buffer, vertex_buffer, bounds);
MeshData data;
DefaultMeshData(&data);
data.nattributes = uvs.data() ? 3 : 2;
data.attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION;
data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data.attributes[0].bytes = positions.data();
data.attributes[1].usage = mjVERTEX_ATTRIBUTE_NORMAL;
data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data.attributes[1].bytes = normals.data();
data.attributes[2].usage = mjVERTEX_ATTRIBUTE_UV;
data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data.attributes[2].bytes = uvs.data();
data.nvertices = positions.size() / 3;
data.nindices = num_indices;
data.indices = indices.data();
data.index_type = mjINDEX_TYPE_UINT;
data.primitive_type = mjPRIM_TYPE_TRIANGLES;
data.compute_bounds = true;
data.release_callback = nullptr;
data.user_data = nullptr;
return std::make_unique<Mesh>(engine, data);
}
} // namespace mujoco
+24 -19
View File
@@ -33,7 +33,6 @@
#include <mujoco/mujoco.h>
#include "experimental/filament/filament/buffer_util.h"
#include "experimental/filament/filament/texture.h"
#include "experimental/filament/filament/vertex_util.h"
namespace mujoco {
@@ -202,10 +201,14 @@ void GuiView::UpdateRenderable() {
}
commands->ScaleClipRects(scale);
// 2 floats for position, 2 floats for uv, 4 bytes for color.
constexpr size_t kExpectedVertexSize =
sizeof(float) * 4 + sizeof(uint8_t) * 4;
int num_elements = 0;
for (int n = 0; n < commands->CmdListsCount; ++n) {
const ImDrawList* cmds = commands->CmdLists[n];
if (sizeof(GuiVertex) != sizeof(cmds->VtxBuffer.Data[0])) {
if (kExpectedVertexSize != sizeof(cmds->VtxBuffer.Data[0])) {
mju_error("Invalid vertex buffer size.");
}
if (sizeof(uint16_t) != sizeof(cmds->IdxBuffer.Data[0])) {
@@ -274,24 +277,26 @@ void GuiView::UpdateRenderable() {
int drawable_index = 0;
for (int n = 0; n < commands->CmdListsCount; ++n) {
const ImDrawList* cmds = commands->CmdLists[n];
auto vfill = [&](std::byte* dst, std::size_t size) {
if (size != cmds->VtxBuffer.size_in_bytes()) {
mju_error("Invalid vertex buffer size.");
}
std::memcpy(dst, cmds->VtxBuffer.Data, size);
};
auto ifill = [&](std::byte* dst, std::size_t size) {
if (size != cmds->IdxBuffer.size_in_bytes()) {
mju_error("Invalid index buffer size.");
}
std::memcpy(dst, cmds->IdxBuffer.Data, size);
};
filament::IndexBuffer* index_buffer =
CreateIndexBuffer<uint16_t>(engine_, cmds->IdxBuffer.Size, ifill);
filament::VertexBuffer* vertex_buffer =
CreateVertexBuffer<GuiVertex>(engine_, cmds->VtxBuffer.Size, vfill);
meshes_.push_back(std::make_unique<Mesh>(engine_, index_buffer, vertex_buffer));
MeshData data;
DefaultMeshData(&data);
data.nattributes = 3;
data.attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION;
data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data.attributes[0].bytes = cmds->VtxBuffer.Data;
data.attributes[1].usage = mjVERTEX_ATTRIBUTE_UV;
data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data.attributes[1].bytes = cmds->VtxBuffer.Data + sizeof(float) * 2;
data.attributes[2].usage = mjVERTEX_ATTRIBUTE_COLOR;
data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_UBYTE4;
data.attributes[2].bytes = cmds->VtxBuffer.Data + sizeof(float) * 4;
data.interleaved = true;
data.nvertices = cmds->VtxBuffer.Size;
data.nindices = cmds->IdxBuffer.Size;
data.indices = cmds->IdxBuffer.Data;
data.index_type = mjINDEX_TYPE_USHORT;
data.primitive_type = mjPRIM_TYPE_TRIANGLES;
meshes_.push_back(std::make_unique<Mesh>(engine_, data));
const auto& mesh = meshes_.back();
int index_offset = 0;
@@ -87,19 +87,16 @@ void ModelObjects::UploadMesh(const mjModel* model, int id) {
meshes_.erase(id);
convex_hulls_.erase(id);
filament::Box bounds;
auto vertex_buffer =
CreateVertexBuffer(engine_, model, id, MeshType::kNormal, &bounds);
auto index_buffer = CreateIndexBuffer(engine_, model, id, MeshType::kNormal);
meshes_[id] =
std::make_unique<Mesh>(engine_, index_buffer, vertex_buffer, bounds);
MeshData data;
DefaultMeshData(&data);
UpdateMeshData(&data, model, id, MeshType::kNormal);
meshes_[id] = std::make_unique<Mesh>(engine_, data);
if (model->mesh_graphadr[id] >= 0) {
vertex_buffer =
CreateVertexBuffer(engine_, model, id, MeshType::kConvexHull, &bounds);
index_buffer = CreateIndexBuffer(engine_, model, id, MeshType::kConvexHull);
convex_hulls_[id] =
std::make_unique<Mesh>(engine_, index_buffer, vertex_buffer, bounds);
MeshData convex_hull_data;
DefaultMeshData(&convex_hull_data);
UpdateMeshData(&convex_hull_data, model, id, MeshType::kConvexHull);
convex_hulls_[id] = std::make_unique<Mesh>(engine_, convex_hull_data);
}
}
@@ -160,13 +157,10 @@ void ModelObjects::UploadHeightField(const mjModel* model, int id) {
height_fields_.erase(id);
filament::Box bounds;
auto vertex_buffer =
CreateVertexBuffer(engine_, model, id, MeshType::kHeightField, &bounds);
auto index_buffer =
CreateIndexBuffer(engine_, model, id, MeshType::kHeightField);
height_fields_[id] =
std::make_unique<Mesh>(engine_, index_buffer, vertex_buffer, bounds);
MeshData data;
DefaultMeshData(&data);
UpdateMeshData(&data, model, id, MeshType::kHeightField);
height_fields_[id] = std::make_unique<Mesh>(engine_, data);
}
const Mesh* ModelObjects::GetMeshBuffer(int data_id) const {
+125 -233
View File
@@ -16,15 +16,11 @@
#include <algorithm>
#include <cfloat>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <vector>
#include <filament/Box.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#include <math/TVecHelpers.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
@@ -40,6 +36,30 @@ using filament::math::float2;
using filament::math::float3;
using filament::math::float4;
struct MeshBuilder {
MeshBuilder(int nvertices) : nvertices(nvertices) {
positions.reserve(nvertices);
orientations.reserve(nvertices);
uvs.reserve(nvertices);
}
void Append(const float3& position, const float4& orientation,
const float2& uv) {
positions.push_back(position);
orientations.push_back(orientation);
uvs.push_back(uv);
bounds_min = min(bounds_min, position);
bounds_max = max(bounds_max, position);
}
int nvertices = 0;
float3 bounds_min = {FLT_MAX, FLT_MAX, FLT_MAX};
float3 bounds_max = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
std::vector<float3> positions;
std::vector<float4> orientations;
std::vector<float2> uvs;
};
static bool UseFaceNormal(const float3& face_normal,
const float3& mesh_normal) {
// clang-format off
@@ -49,74 +69,42 @@ static bool UseFaceNormal(const float3& face_normal,
// clang-format on
}
static void UpdateBounds(const float3& v, float3* vmin, float3* vmax) {
vmin->x = std::min(vmin->x, v.x);
vmin->y = std::min(vmin->y, v.y);
vmin->z = std::min(vmin->z, v.z);
vmax->x = std::max(vmax->x, v.x);
vmax->y = std::max(vmax->y, v.y);
vmax->z = std::max(vmax->z, v.z);
}
template <typename T>
static void FillConvexHullBuffer(T* ptr, std::size_t num, const mjModel* model,
int meshid, float3* vmin, float3* vmax) {
static void FillConvexHullBuffer(MeshBuilder& builder, const mjModel* model,
int meshid) {
const int numvert = model->mesh_graph[model->mesh_graphadr[meshid]];
const int numface = model->mesh_graph[model->mesh_graphadr[meshid] + 1];
const int vertadr = model->mesh_vertadr[meshid];
const float* vertices = model->mesh_vert + (3 * vertadr);
const int texcoordadr = model->mesh_texcoordadr[meshid];
const float* texcoords = model->mesh_texcoord + (2 * texcoordadr);
if (num != numface * 3) {
mju_error("Invalid vertex count.");
if (builder.nvertices != numface * 3) {
mju_error("Invalid vertex count (%d vs %d).", builder.nvertices, numface * 3);
return;
}
for (int face = 0; face < numface; ++face) {
int j =
model->mesh_graphadr[meshid] + 2 + 3 * numvert + 3 * numface + 3 * face;
const int dataadr = model->mesh_graphadr[meshid] + 2;
const int vertadr = model->mesh_vertadr[meshid];
const float* vertices = model->mesh_vert + (3 * vertadr);
const int texcoordadr = model->mesh_texcoordadr[meshid];
const float* texcoords = texcoordadr >= 0 ? model->mesh_texcoord + (2 * texcoordadr) : nullptr;
for (int face = 0; face < numface; ++face) {
const int j = dataadr + (3 * numvert) + (3 * numface) + (3 * face);
const float3 p1 = ReadFloat3(vertices, model->mesh_graph[j + 0]);
const float3 p2 = ReadFloat3(vertices, model->mesh_graph[j + 1]);
const float3 p3 = ReadFloat3(vertices, model->mesh_graph[j + 2]);
const float4 orientation = CalculateOrientation(p1, p2, p3);
UpdateBounds(p1, vmin, vmax);
UpdateBounds(p2, vmin, vmax);
UpdateBounds(p3, vmin, vmax);
ptr->position = p1;
ptr->orientation = orientation;
if constexpr (T::kHasUv) {
ptr->uv = ReadFloat2(texcoords, model->mesh_graph[j + 0]);
}
++ptr;
ptr->position = p2;
ptr->orientation = orientation;
if constexpr (T::kHasUv) {
ptr->uv = ReadFloat2(texcoords, model->mesh_graph[j + 1]);
}
++ptr;
ptr->position = p3;
ptr->orientation = orientation;
if constexpr (T::kHasUv) {
ptr->uv = ReadFloat2(texcoords, model->mesh_graph[j + 2]);
}
++ptr;
const float2 uv1 = texcoords ? ReadFloat2(texcoords, model->mesh_graph[j + 0]) : float2(0, 0);
const float2 uv2 = texcoords ? ReadFloat2(texcoords, model->mesh_graph[j + 1]) : float2(0, 0);
const float2 uv3 = texcoords ? ReadFloat2(texcoords, model->mesh_graph[j + 2]) : float2(0, 0);
builder.Append(p1, orientation, uv1);
builder.Append(p2, orientation, uv2);
builder.Append(p3, orientation, uv3);
}
}
template <typename T>
static void FillMeshBuffer(T* ptr, std::size_t num, const mjModel* model,
int meshid, float3* vmin, float3* vmax) {
static void FillMeshBuffer(MeshBuilder& builder, const mjModel* model, int meshid) {
const int faceadr = model->mesh_faceadr[meshid];
const int facenum = model->mesh_facenum[meshid];
if (num != facenum * 3) {
mju_error("Invalid vertex count.");
if (builder.nvertices != facenum * 3) {
mju_error("Invalid vertex count (%d vs %d).", builder.nvertices, facenum * 3);
return;
}
@@ -125,7 +113,7 @@ static void FillMeshBuffer(T* ptr, std::size_t num, const mjModel* model,
const int normaladr = model->mesh_normaladr[meshid];
const float* normals = model->mesh_normal + 3 * normaladr;
const int texcoordadr = model->mesh_texcoordadr[meshid];
const float* texcoords = model->mesh_texcoord + (2 * texcoordadr);
const float* texcoords = texcoordadr >= 0 ? model->mesh_texcoord + (2 * texcoordadr) : nullptr;
for (int i = 0; i < facenum; ++i) {
const int face = 3 * (faceadr + i);
@@ -133,70 +121,41 @@ static void FillMeshBuffer(T* ptr, std::size_t num, const mjModel* model,
const float3 p1 = ReadFloat3(vertices, model->mesh_face[face + 0]);
const float3 p2 = ReadFloat3(vertices, model->mesh_face[face + 1]);
const float3 p3 = ReadFloat3(vertices, model->mesh_face[face + 2]);
UpdateBounds(p1, vmin, vmax);
UpdateBounds(p2, vmin, vmax);
UpdateBounds(p3, vmin, vmax);
const float3 face_normal = CalculateNormal(p1, p2, p3);
const float3 n1 = ReadFloat3(normals, model->mesh_facenormal[face + 0]);
const float3 n2 = ReadFloat3(normals, model->mesh_facenormal[face + 1]);
const float3 n3 = ReadFloat3(normals, model->mesh_facenormal[face + 2]);
const float2 uv1 = texcoords ? ReadFloat2(texcoords, model->mesh_facetexcoord[face + 0]) : float2(0, 0);
const float2 uv2 = texcoords ? ReadFloat2(texcoords, model->mesh_facetexcoord[face + 1]) : float2(0, 0);
const float2 uv3 = texcoords ? ReadFloat2(texcoords, model->mesh_facetexcoord[face + 2]) : float2(0, 0);
ptr->position = p1;
if constexpr (T::kHasUv) {
ptr->orientation = CalculateOrientation(n1);
ptr->uv = ReadFloat2(texcoords, model->mesh_facetexcoord[face + 0]);
} else if (UseFaceNormal(face_normal, n1)) {
ptr->orientation = CalculateOrientation(face_normal);
if (UseFaceNormal(face_normal, n1)) {
builder.Append(p1, CalculateOrientation(face_normal), uv1);
} else {
ptr->orientation = CalculateOrientation(n1);
builder.Append(p1, CalculateOrientation(n1), uv1);
}
++ptr;
ptr->position = p2;
if constexpr (T::kHasUv) {
ptr->orientation = CalculateOrientation(n2);
ptr->uv = ReadFloat2(texcoords, model->mesh_facetexcoord[face + 1]);
} else if (UseFaceNormal(face_normal, n2)) {
ptr->orientation = CalculateOrientation(face_normal);
if (UseFaceNormal(face_normal, n2)) {
builder.Append(p2, CalculateOrientation(face_normal), uv2);
} else {
ptr->orientation = CalculateOrientation(n2);
builder.Append(p2, CalculateOrientation(n2), uv2);
}
++ptr;
ptr->position = p3;
if constexpr (T::kHasUv) {
ptr->orientation = CalculateOrientation(n3);
ptr->uv = ReadFloat2(texcoords, model->mesh_facetexcoord[face + 2]);
} else if (UseFaceNormal(face_normal, n3)) {
ptr->orientation = CalculateOrientation(face_normal);
if (UseFaceNormal(face_normal, n3)) {
builder.Append(p3, CalculateOrientation(face_normal), uv3);
} else {
ptr->orientation = CalculateOrientation(n3);
builder.Append(p3, CalculateOrientation(n3), uv3);
}
++ptr;
}
}
static void FillHeightFieldBuffer(VertexNoUv* ptr, std::size_t num,
const mjModel* model, int hfieldid,
float3* vmin, float3* vmax) {
int count = 0;
static void FillHeightFieldBuffer(MeshBuilder& builder, const mjModel* model,
int hfieldid) {
auto append_tri = [&](float3 a, float3 b, float3 c) {
float4 orientation = CalculateOrientation(a, b, c);
ptr[count].position = a;
ptr[count].orientation = orientation;
++count;
ptr[count].position = b;
ptr[count].orientation = orientation;
++count;
ptr[count].position = c;
ptr[count].orientation = orientation;
++count;
UpdateBounds(a, vmin, vmax);
UpdateBounds(b, vmin, vmax);
UpdateBounds(c, vmin, vmax);
builder.Append(a, orientation, float2(0, 0));
builder.Append(b, orientation, float2(0, 0));
builder.Append(c, orientation, float2(0, 0));
};
auto append_quad = [&](float3 a, float3 b, float3 c, float3 d) {
append_tri(a, b, d);
@@ -314,9 +273,6 @@ static void FillHeightFieldBuffer(VertexNoUv* ptr, std::size_t num,
{x1, y0, -sz[3]});
}
}
if (count != num) {
mju_error("Vertex count mismatch.");
}
}
static int CalculateHeightFieldVertexCount(const mjModel* model, int hfieldid) {
@@ -340,149 +296,85 @@ static int CalculateHeightFieldVertexCount(const mjModel* model, int hfieldid) {
return total_count;
}
template <typename T, typename FillFn>
static filament::VertexBuffer* CreateVertexBuffer(filament::Engine* engine,
const mjModel* model, int id,
int vertex_count,
FillFn fill_fn,
filament::Box* bounds) {
float3 vmin = {FLT_MAX, FLT_MAX, FLT_MAX};
float3 vmax = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
filament::VertexBuffer* buffer = CreateVertexBuffer<T>(
engine, vertex_count, [&](std::byte* buffer, std::size_t num_bytes) {
auto* ptr = reinterpret_cast<T*>(buffer);
fill_fn(ptr, num_bytes / sizeof(T), model, id, &vmin, &vmax);
});
bounds->set(vmin, vmax);
return buffer;
static bool HasUvs(const mjModel* model, int id, MeshType mesh_type) {
return mesh_type != MeshType::kHeightField &&
model->mesh_texcoordadr[id] >= 0;
}
filament::VertexBuffer* CreateVertexBuffer(filament::Engine* engine,
const mjModel* model, int id,
MeshType mesh_type,
filament::Box* bounds) {
if (id < 0) {
mju_error("Invalid mesh index %d", id);
return nullptr;
}
int vertex_count = 0;
static bool IsValidIndex(const mjModel* model, int id, MeshType mesh_type) {
switch (mesh_type) {
case MeshType::kNormal:
if (id >= model->nmesh) {
mju_error("Invalid mesh index %d", id);
return nullptr;
}
vertex_count = 3 * model->mesh_facenum[id];
break;
return id >= 0 && id < model->nmesh;
case MeshType::kConvexHull:
if (id >= model->nmesh) {
mju_error("Invalid mesh index %d", id);
return nullptr;
}
vertex_count = 3 * model->mesh_graph[model->mesh_graphadr[id] + 1];
break;
return id >= 0 && id < model->nmesh;
case MeshType::kHeightField:
if (id >= model->nhfield) {
mju_error("Invalid height field index %d", id);
return nullptr;
}
vertex_count = CalculateHeightFieldVertexCount(model, id);
break;
return id >= 0 && id < model->nhfield;
}
if (vertex_count == 0) {
mju_error("Vertex count is zero.");
return nullptr;
}
const bool has_texcoords = mesh_type == MeshType::kHeightField
? false
: model->mesh_texcoordadr[id] >= 0;
if (has_texcoords) {
using VertexType = VertexWithUv;
switch (mesh_type) {
case MeshType::kNormal:
return CreateVertexBuffer<VertexType>(engine, model, id, vertex_count,
FillMeshBuffer<VertexType>,
bounds);
break;
case MeshType::kConvexHull:
return CreateVertexBuffer<VertexType>(engine, model, id, vertex_count,
FillConvexHullBuffer<VertexType>,
bounds);
break;
case MeshType::kHeightField:
mju_error("Height fields do not support UV coordinates.");
return nullptr;
}
} else {
using VertexType = VertexNoUv;
switch (mesh_type) {
case MeshType::kNormal:
return CreateVertexBuffer<VertexType>(engine, model, id, vertex_count,
FillMeshBuffer<VertexType>,
bounds);
break;
case MeshType::kConvexHull:
return CreateVertexBuffer<VertexType>(engine, model, id, vertex_count,
FillConvexHullBuffer<VertexType>,
bounds);
break;
case MeshType::kHeightField:
return CreateVertexBuffer<VertexType>(engine, model, id, vertex_count,
FillHeightFieldBuffer, bounds);
break;
}
}
return nullptr;
}
filament::IndexBuffer* CreateIndexBuffer(filament::Engine* engine,
const mjModel* model, int id,
MeshType mesh_type) {
if (id < 0) {
mju_error("Invalid index %d", id);
return nullptr;
}
int index_count = 0;
static int GetNumVertices(const mjModel* model, int id, MeshType mesh_type) {
switch (mesh_type) {
case MeshType::kNormal:
if (id >= model->nmesh) {
mju_error("Invalid mesh index %d", id);
return nullptr;
}
index_count = 3 * model->mesh_facenum[id];
return 3 * model->mesh_facenum[id];
case MeshType::kConvexHull:
return 3 * model->mesh_graph[model->mesh_graphadr[id] + 1];
case MeshType::kHeightField:
return CalculateHeightFieldVertexCount(model, id);
}
}
void UpdateMeshData(MeshData* data, const mjModel* model, int id,
MeshType mesh_type) {
if (!IsValidIndex(model, id, mesh_type)) {
mju_error("Invalid index %d for type %d", id, mesh_type);
return;
}
const int num_vertices = GetNumVertices(model, id, mesh_type);
const bool has_uvs = HasUvs(model, id, mesh_type);
MeshBuilder* builder = new MeshBuilder(num_vertices);
data->user_data = builder;
data->release_callback = [](void* user_data) {
delete static_cast<MeshBuilder*>(user_data);
};
switch (mesh_type) {
case MeshType::kNormal:
FillMeshBuffer(*builder, model, id);
break;
case MeshType::kConvexHull:
if (id >= model->nmesh) {
mju_error("Invalid mesh index %d", id);
return nullptr;
}
index_count = 3 * model->mesh_graph[model->mesh_graphadr[id] + 1];
FillConvexHullBuffer(*builder, model, id);
break;
case MeshType::kHeightField:
if (id >= model->nhfield) {
mju_error("Invalid height field index %d", id);
return nullptr;
}
index_count = CalculateHeightFieldVertexCount(model, id);
FillHeightFieldBuffer(*builder, model, id);
break;
}
if (index_count == 0) {
mju_error("Index count is zero.");
return nullptr;
}
if (index_count >= std::numeric_limits<uint16_t>::max()) {
return CreateIndexBuffer<uint32_t>(engine, index_count,
FillSequence<uint32_t>);
} else {
return CreateIndexBuffer<uint16_t>(engine, index_count,
FillSequence<uint16_t>);
data->primitive_type = mjPRIM_TYPE_TRIANGLES;
data->nvertices = num_vertices;
data->nindices = data->nvertices;
data->indices = nullptr;
data->index_type = data->nvertices >= std::numeric_limits<uint16_t>::max()
? mjINDEX_TYPE_UINT
: mjINDEX_TYPE_USHORT;
data->nattributes = has_uvs ? 3 : 2;
data->attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION;
data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data->attributes[0].bytes = builder->positions.data();
data->attributes[1].usage = mjVERTEX_ATTRIBUTE_TANGENTS;
data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
data->attributes[1].bytes = builder->orientations.data();
if (has_uvs) {
data->attributes[2].usage = mjVERTEX_ATTRIBUTE_UV;
data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data->attributes[2].bytes = builder->uvs.data();
}
data->bounds_min[0] = builder->bounds_min.x;
data->bounds_min[1] = builder->bounds_min.y;
data->bounds_min[2] = builder->bounds_min.z;
data->bounds_max[0] = builder->bounds_max.x;
data->bounds_max[1] = builder->bounds_max.y;
data->bounds_max[2] = builder->bounds_max.z;
}
} // namespace mujoco
@@ -17,16 +17,12 @@
#include <string_view>
#include <filament/Box.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mujoco.h>
#include "experimental/filament/filament/buffer_util.h"
namespace mujoco {
@@ -37,16 +33,9 @@ enum class MeshType {
kHeightField,
};
// Generates a filament VertexBuffer for a given mesh in the mjModel.
filament::VertexBuffer* CreateVertexBuffer(filament::Engine* engine,
const mjModel* model, int id,
MeshType mesh_type,
filament::Box* bounds);
// Generates a filament IndexBuffer for a given mesh in the mjModel.
filament::IndexBuffer* CreateIndexBuffer(filament::Engine* engine,
const mjModel* model, int id,
MeshType mesh_type);
// Populates the given MeshData with data for the element in the model.
void UpdateMeshData(MeshData* data, const mjModel* model, int id,
MeshType mesh_type);
// Reads a value with the given name from the mjModel's data sections. The
// default_value is returned if the named element is not found.
@@ -73,13 +73,13 @@ void Renderables::Update(int index, MeshPtr mesh) {
void Renderables::Append(const Mesh* mesh) {
utils::Entity entity = CreateEntity(mesh);
entities_.push_back(entity);
meshes_.emplace_back(nullptr, mesh);
meshes_.push_back({nullptr, mesh});
}
void Renderables::Append(MeshPtr mesh) {
utils::Entity entity = CreateEntity(mesh.get());
entities_.push_back(entity);
meshes_.emplace_back(std::move(mesh), mesh.get());
meshes_.push_back({std::move(mesh), mesh.get()});
}
utils::Entity Renderables::CreateEntity(const Mesh* mesh) {
@@ -15,7 +15,6 @@
#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_VERTEX_UTIL_H_
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_VERTEX_UTIL_H_
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
@@ -37,59 +36,6 @@ filament::math::float4 CalculateOrientation(
const filament::math::float3& p2,
const filament::math::float3& p3);
// A standard vertex with no UV coordinates.
struct VertexNoUv {
VertexNoUv() = default;
VertexNoUv(filament::math::float3 position,
filament::math::float4 orientation)
: position(position), orientation(orientation) {}
filament::math::float3 position;
filament::math::float4 orientation;
static constexpr bool kHasPosition = true;
static constexpr bool kHasPosition2d = false;
static constexpr bool kHasOrientation = true;
static constexpr bool kHasUv = false;
static constexpr bool kHasColor = false;
};
// A standard vertex with UV coordinates.
struct VertexWithUv {
VertexWithUv() = default;
VertexWithUv(filament::math::float3 position,
filament::math::float4 orientation, filament::math::float2 uv)
: position(position), orientation(orientation), uv(uv) {}
filament::math::float3 position;
filament::math::float4 orientation;
filament::math::float2 uv;
static constexpr bool kHasPosition = true;
static constexpr bool kHasPosition2d = false;
static constexpr bool kHasOrientation = true;
static constexpr bool kHasUv = true;
static constexpr bool kHasColor = false;
};
// A vertex for rendering GUI elements.
struct GuiVertex {
GuiVertex() = default;
GuiVertex(filament::math::float2 position, filament::math::float2 uv,
filament::math::ubyte4 color)
: position(position), uv(uv), color(color) {}
filament::math::float2 position;
filament::math::float2 uv;
filament::math::ubyte4 color;
static constexpr bool kHasPosition = false;
static constexpr bool kHasPosition2d = true;
static constexpr bool kHasOrientation = false;
static constexpr bool kHasUv = true;
static constexpr bool kHasColor = true;
};
} // namespace mujoco
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_VERTEX_UTIL_H_