move Geometry asset builder from header to own implementation

This commit is contained in:
Nick Fisher
2024-11-30 13:58:02 +08:00
parent 600ab1ad28
commit d53a8b20b8
2 changed files with 322 additions and 274 deletions

View File

@@ -17,287 +17,36 @@ namespace thermion
class GeometrySceneAssetBuilder
{
public:
GeometrySceneAssetBuilder(filament::Engine *engine) : mEngine(engine) {}
GeometrySceneAssetBuilder(filament::Engine *engine);
GeometrySceneAssetBuilder &vertices(const float *vertices, uint32_t count)
{
mVertices->resize(count);
std::copy(vertices, vertices + count, mVertices->data());
mNumVertices = count;
return *this;
}
GeometrySceneAssetBuilder &normals(const float *normals, uint32_t count)
{
if (normals)
{
mNormals->resize(count);
std::copy(normals, normals + count, mNormals->data());
}
else
{
mNormals->clear();
}
mNumNormals = count;
return *this;
}
GeometrySceneAssetBuilder &uvs(const float *uvs, uint32_t count)
{
if (uvs)
{
mUVs->resize(count);
std::copy(uvs, uvs + count, mUVs->data());
}
else
{
mUVs->clear();
}
mNumUVs = count;
return *this;
}
GeometrySceneAssetBuilder &indices(const uint16_t *indices, uint32_t count)
{
mIndices->resize(count);
std::copy(indices, indices + count, mIndices->data());
mNumIndices = count;
return *this;
}
GeometrySceneAssetBuilder &materials(filament::MaterialInstance **materials, size_t materialInstanceCount)
{
mMaterialInstances = materials;
mMaterialInstanceCount = materialInstanceCount;
return *this;
}
GeometrySceneAssetBuilder &primitiveType(filament::RenderableManager::PrimitiveType type)
{
mPrimitiveType = type;
return *this;
}
std::unique_ptr<GeometrySceneAsset> build()
{
Log("Starting build. Validating inputs...");
if (!validate())
{
Log("Validation failed!");
return nullptr;
}
Log("Creating buffers...");
auto [vertexBuffer, indexBuffer] = createBuffers();
if (!vertexBuffer || !indexBuffer)
{
Log("Failed to create buffers: VB=%p, IB=%p", vertexBuffer, indexBuffer);
return nullptr;
}
Log("Buffers created successfully: VB=%p, IB=%p", vertexBuffer, indexBuffer);
Log("Creating entity...");
auto entity = utils::EntityManager::get().create();
Log("Entity created: %d", entity.getId());
Box boundingBox = computeBoundingBox();
Log("Computed bounding box: min={%f,%f,%f}, max={%f,%f,%f}",
boundingBox.getMin().x, boundingBox.getMin().y, boundingBox.getMin().z,
boundingBox.getMax().x, boundingBox.getMax().y, boundingBox.getMax().z);
auto asset = std::make_unique<GeometrySceneAsset>(
false,
mEngine,
vertexBuffer,
indexBuffer,
mMaterialInstances,
mMaterialInstanceCount,
mPrimitiveType,
boundingBox);
Log("Asset created: %p", asset.get());
return asset;
}
GeometrySceneAssetBuilder &vertices(const float *vertices, uint32_t count);
GeometrySceneAssetBuilder &normals(const float *normals, uint32_t count);
GeometrySceneAssetBuilder &uvs(const float *uvs, uint32_t count);
GeometrySceneAssetBuilder &indices(const uint16_t *indices, uint32_t count);
GeometrySceneAssetBuilder &materials(filament::MaterialInstance **materials, size_t materialInstanceCount);
GeometrySceneAssetBuilder &primitiveType(filament::RenderableManager::PrimitiveType type);
std::unique_ptr<GeometrySceneAsset> build();
private:
Box computeBoundingBox()
{
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
Box box;
for (uint32_t i = 0; i < mNumVertices; i += 3)
{
minX = std::min(mVertices->at(i), minX);
minY = std::min(mVertices->at(i + 1), minY);
minZ = std::min(mVertices->at(i + 2), minZ);
maxX = std::max(mVertices->at(i), maxX);
maxY = std::max(mVertices->at(i + 1), maxY);
maxZ = std::max(mVertices->at(i + 2), maxZ);
}
const filament::math::float3 min {minX, minY, minZ};
const filament::math::float3 max {maxX, maxY, maxZ};
box.set(min, max);
return box;
}
Box computeBoundingBox();
std::pair<filament::VertexBuffer *, filament::IndexBuffer *> createBuffers();
std::pair<filament::VertexBuffer *, filament::IndexBuffer *> createBuffers()
{
auto indexBuffer = IndexBuffer::Builder()
.indexCount(mNumIndices)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*mEngine);
indexBuffer->setBuffer(*mEngine,
IndexBuffer::BufferDescriptor(
mIndices->data(),
mNumIndices * sizeof(uint16_t),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<float> *>(data);
},
mIndices));
if (mUVs->empty())
{
mUVs->resize(mNumVertices);
std::fill(mUVs->begin(), mUVs->end(), 0.0f);
}
auto dummyColors = new std::vector<filament::math::float4>(
mNumVertices, filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f});
auto vertexBufferBuilder =
VertexBuffer::Builder()
.vertexCount(mNumVertices)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
.attribute(VertexAttribute::UV0, 1, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::UV1, 2, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::COLOR, 3, VertexBuffer::AttributeType::FLOAT4);
if (!mNormals->empty())
{
vertexBufferBuilder.bufferCount(5)
.attribute(VertexAttribute::TANGENTS, 4, VertexBuffer::AttributeType::FLOAT4);
}
else
{
vertexBufferBuilder = vertexBufferBuilder.bufferCount(4);
}
auto vertexBuffer = vertexBufferBuilder.build(*mEngine);
vertexBuffer->setBufferAt(*mEngine, 0,
VertexBuffer::BufferDescriptor(
mVertices->data(), mNumVertices * sizeof(float),
[](void *, size_t, void *) {}));
vertexBuffer->setBufferAt(*mEngine, 1,
VertexBuffer::BufferDescriptor(
mUVs->data(), mUVs->size() * sizeof(float),
[](void *, size_t, void *data)
{
},
mUVs));
vertexBuffer->setBufferAt(*mEngine, 2,
VertexBuffer::BufferDescriptor(
mUVs->data(), mUVs->size() * sizeof(float),
[](void *, size_t, void *data) {
delete static_cast<std::vector<float> *>(data);
},
mUVs));
vertexBuffer->setBufferAt(*mEngine, 3,
VertexBuffer::BufferDescriptor(
dummyColors->data(), dummyColors->size() * sizeof(math::float4),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<math::float4> *>(data);
},
dummyColors));
if (!mNormals->empty())
{
assert(mPrimitiveType == RenderableManager::PrimitiveType::TRIANGLES);
std::vector<filament::math::ushort3> triangles;
for (uint32_t i = 0; i < mNumIndices; i += 3)
{
triangles.push_back({mIndices->at(i),
mIndices->at(i + 1),
mIndices->at(i + 2)});
}
auto &builder = geometry::SurfaceOrientation::Builder()
.vertexCount(mNumVertices)
.normals((filament::math::float3 *)mNormals->data())
.positions((filament::math::float3 *)mVertices->data())
.triangleCount(triangles.size())
.triangles(triangles.data());
auto orientation = builder.build();
auto quats = new std::vector<filament::math::quatf>(mNumVertices);
orientation->getQuats(quats->data(), mNumVertices);
vertexBuffer->setBufferAt(*mEngine, 4,
VertexBuffer::BufferDescriptor(
quats->data(), quats->size() * sizeof(math::quatf),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<math::quatf> *>(data);
},
quats));
}
return {vertexBuffer, indexBuffer};
}
bool validate() const
{
if (!mEngine)
{
Log("Validation failed: No engine");
return false;
}
if (mVertices->empty() || mNumVertices == 0)
{
Log("Validation failed: No vertices (empty=%d, count=%d)", mVertices->empty(), mNumVertices);
return false;
}
if (mNumNormals > 0 && !mNormals->empty() && mNumNormals != mNumVertices)
{
Log("Validation failed: Normal count mismatch (normals=%d, vertices=%d)", mNumNormals, mNumVertices);
return false;
}
if (mNumUVs > 0 && !mUVs->empty() && mNumUVs != mNumVertices)
{
Log("Validation failed: UV count mismatch (uvs=%d, vertices=%d)", mNumUVs, mNumVertices);
return false;
}
if (mIndices->empty() || mNumIndices == 0)
{
Log("Validation failed: No indices (empty=%d, count=%d)", mIndices->empty(), mNumIndices);
return false;
}
Log("Validation passed: vertices=%d, normals=%s, uvs=%d, indices=%d",
mNumVertices,
(!mNormals->empty() ? "yes" : "no"),
mNumUVs,
mNumIndices);
return true;
}
bool validate() const;
filament::Engine *mEngine = nullptr;
std::vector<float> *mVertices = new std::vector<float>();
std::vector<float> *mNormals = new std::vector<float>();
std::vector<float> *mUVs = new std::vector<float>();
std::vector<filament::math::float3> *mVertices = new std::vector<filament::math::float3>();
std::vector<filament::math::float3> *mNormals = new std::vector<filament::math::float3>();
std::vector<filament::math::float2> *mUVs = new std::vector<filament::math::float2>();
std::vector<uint16_t> *mIndices = new std::vector<uint16_t>;
uint32_t mNumVertices = 0;
uint32_t mNumNormals = 0;
uint32_t mNumUVs = 0;
uint32_t mNumIndices = 0;
filament::MaterialInstance **mMaterialInstances = nullptr;
size_t mMaterialInstanceCount = 0;
filament::gltfio::MaterialProvider *mMaterialProvider = nullptr;

View File

@@ -0,0 +1,299 @@
#pragma once
#include <memory>
#include <vector>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/geometry/SurfaceOrientation.h>
#include <filament/Box.h>
#include <gltfio/MaterialProvider.h>
#include "scene/GeometrySceneAssetBuilder.hpp"
#include "scene/GeometrySceneAsset.hpp"
#include "Log.hpp"
namespace thermion
{
GeometrySceneAssetBuilder::GeometrySceneAssetBuilder(filament::Engine *engine) : mEngine(engine)
{
}
GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::vertices(const float *vertices, uint32_t count)
{
if (count > 0)
{
mVertices->resize(count / 3);
for (int i = 0; i < mVertices->size(); i++)
{
mVertices->at(i) = filament::math::float3{vertices[(i * 3)], vertices[(i * 3) + 1], vertices[(i * 3) + 2]};
}
}
return *this;
}
GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::normals(const float *normals, uint32_t count)
{
if (count > 0)
{
mNormals->resize(count / 3);
for (int i = 0; i < mNormals->size(); i++)
{
mNormals->at(i) = filament::math::float3{normals[i * 3], normals[(i * 3) + 1], normals[(i * 3) + 2]};
}
}
return *this;
}
GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::uvs(const float *uvs, uint32_t count)
{
if (count > 0)
{
mUVs->resize(count / 2);
for (int i = 0; i < mUVs->size(); i++)
{
mUVs->at(i) = filament::math::float2{uvs[i * 2], uvs[(i * 2) + 1]};
}
}
return *this;
}
GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::indices(const uint16_t *indices, uint32_t count)
{
if (count > 0)
{
mIndices->resize(count);
for (int i = 0; i < mIndices->size(); i++)
{
mIndices->at(i) = indices[i];
}
}
return *this;
}
GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::materials(filament::MaterialInstance **materials, size_t materialInstanceCount)
{
mMaterialInstances = materials;
mMaterialInstanceCount = materialInstanceCount;
return *this;
}
GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::primitiveType(filament::RenderableManager::PrimitiveType type)
{
mPrimitiveType = type;
return *this;
}
std::unique_ptr<GeometrySceneAsset> GeometrySceneAssetBuilder::build()
{
Log("Starting build. Validating inputs...");
if (!validate())
{
Log("Validation failed!");
return nullptr;
}
Log("Creating buffers...");
auto [vertexBuffer, indexBuffer] = createBuffers();
if (!vertexBuffer || !indexBuffer)
{
Log("Failed to create buffers: VB=%p, IB=%p", vertexBuffer, indexBuffer);
return nullptr;
}
Log("Buffers created successfully: VB=%p, IB=%p", vertexBuffer, indexBuffer);
Box boundingBox = computeBoundingBox();
Log("Computed bounding box: min={%f,%f,%f}, max={%f,%f,%f}",
boundingBox.getMin().x, boundingBox.getMin().y, boundingBox.getMin().z,
boundingBox.getMax().x, boundingBox.getMax().y, boundingBox.getMax().z);
auto asset = std::make_unique<GeometrySceneAsset>(
false,
mEngine,
vertexBuffer,
indexBuffer,
mMaterialInstances,
mMaterialInstanceCount,
mPrimitiveType,
boundingBox);
Log("Asset created: %p", asset.get());
return asset;
}
Box GeometrySceneAssetBuilder::computeBoundingBox()
{
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
Box box;
for (auto &vertex : *mVertices)
{
minX = std::min(vertex.x, minX);
minY = std::min(vertex.y, minY);
minZ = std::min(vertex.z, minZ);
maxX = std::max(vertex.x, maxX);
maxY = std::max(vertex.y, maxY);
maxZ = std::max(vertex.z, maxZ);
}
const filament::math::float3 min{minX, minY, minZ};
const filament::math::float3 max{maxX, maxY, maxZ};
box.set(min, max);
return box;
}
std::pair<filament::VertexBuffer *, filament::IndexBuffer *> GeometrySceneAssetBuilder::createBuffers()
{
auto indexBuffer = IndexBuffer::Builder()
.indexCount(mIndices->size())
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*mEngine);
indexBuffer->setBuffer(*mEngine,
IndexBuffer::BufferDescriptor(
mIndices->data(),
mIndices->size() * sizeof(uint16_t),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<uint16_t> *>(data);
},
mIndices));
if (mUVs->empty())
{
mUVs->resize(mVertices->size());
std::fill(mUVs->begin(), mUVs->end(), filament::math::float2{0.0f, 0.0f});
}
auto vertexBufferBuilder =
VertexBuffer::Builder()
.vertexCount(mVertices->size())
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
.attribute(VertexAttribute::UV0, 1, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::UV1, 2, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::COLOR, 3, VertexBuffer::AttributeType::FLOAT4);
if (!mNormals->empty())
{
vertexBufferBuilder.bufferCount(5)
.attribute(VertexAttribute::TANGENTS, 4, VertexBuffer::AttributeType::FLOAT4);
}
else
{
vertexBufferBuilder = vertexBufferBuilder.bufferCount(4);
}
auto vertexBuffer = vertexBufferBuilder.build(*mEngine);
vertexBuffer->setBufferAt(*mEngine, 0,
VertexBuffer::BufferDescriptor(
mVertices->data(), mVertices->size() * sizeof(filament::math::float3),
[](void *, size_t, void *) {
}));
vertexBuffer->setBufferAt(*mEngine, 1,
VertexBuffer::BufferDescriptor(
mUVs->data(), mUVs->size() * sizeof(filament::math::float2),
[](void *, size_t, void *data) {
},
mUVs));
vertexBuffer->setBufferAt(*mEngine, 2,
VertexBuffer::BufferDescriptor(
mUVs->data(), mUVs->size() * sizeof(filament::math::float2),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<filament::math::float2> *>(data);
},
mUVs));
auto dummyColors = new std::vector<filament::math::float4>(
mVertices->size(), filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f});
vertexBuffer->setBufferAt(*mEngine, 3,
VertexBuffer::BufferDescriptor(
dummyColors->data(), dummyColors->size() * sizeof(math::float4),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<math::float4> *>(data);
},
dummyColors));
if (mNormals->size() > 0)
{
std::vector<filament::math::ushort3> triangles;
for (uint32_t i = 0; i < mIndices->size(); i += 3)
{
triangles.push_back({mIndices->at(i),
mIndices->at(i + 1),
mIndices->at(i + 2)});
}
geometry::SurfaceOrientation::Builder builder;
builder.vertexCount(mVertices->size());
builder.normals(mNormals->data());
builder.positions(mVertices->data());
builder.triangleCount(triangles.size());
builder.triangles(triangles.data());
auto orientation = builder.build();
auto quats = new std::vector<filament::math::quatf>(mVertices->size());
orientation->getQuats(quats->data(), mVertices->size());
vertexBuffer->setBufferAt(*mEngine, 4,
VertexBuffer::BufferDescriptor(
quats->data(), quats->size() * sizeof(math::quatf),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<math::quatf> *>(data);
},
quats));
}
return {vertexBuffer, indexBuffer};
}
bool GeometrySceneAssetBuilder::validate() const
{
if (!mEngine)
{
Log("Validation failed: No engine");
return false;
}
if (mVertices->empty())
{
Log("Validation failed: No vertices (empty=%d, count=%d)", mVertices->empty(), mVertices->size());
return false;
}
if (!mNormals->empty())
{
assert(mPrimitiveType == RenderableManager::PrimitiveType::TRIANGLES);
}
if (!mNormals->empty() && mNormals->size() != mVertices->size())
{
Log("Validation failed: Normal count mismatch (normals=%d, vertices=%d)", mNormals->size(), mVertices->size());
return false;
}
if (!mUVs->empty() && mUVs->size() != mVertices->size() * 2)
{
Log("Validation failed: UV count mismatch (uvs=%d, vertices=%d)", mUVs->size(), mVertices->size());
return false;
}
if (mIndices->empty())
{
Log("Validation failed: No indices (empty=%d, count=%d)", mIndices->empty(), mIndices->size());
return false;
}
Log("Validation passed: vertices=%d, normals=%s, uvs=%d, indices=%d",
mVertices->size(),
(!mNormals->empty() ? "yes" : "no"),
mUVs->size(),
mIndices->size());
return true;
}
} // namespace thermion